paperclip-facecrop 0.0.6 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +61 -26
 - data/README_example_b.jpg +0 -0
 - data/lib/detectors/face_com.rb +27 -0
 - data/lib/detectors/opencv.rb +44 -0
 - data/lib/{opencv_ext.rb → detectors/opencv_ext.rb} +2 -2
 - data/lib/face_crop.rb +86 -0
 - data/lib/paperclip-facecrop.rb +2 -149
 - data/lib/processor.rb +143 -0
 - data/lib/railtie.rb +22 -0
 - data/paperclip-facecrop.gemspec +1 -2
 - metadata +36 -65
 
    
        data/README.md
    CHANGED
    
    | 
         @@ -2,15 +2,30 @@ Paperclip::FaceCrop 
     | 
|
| 
       2 
2 
     | 
    
         
             
            ====================
         
     | 
| 
       3 
3 
     | 
    
         
             
            `Paperclip::FaceCrop` is a [Paperclip][paperclip] processor that is aware of the faces found on the image 
         
     | 
| 
       4 
4 
     | 
    
         
             
            so that they aren't cropped or aren't shown too small while generating the thumbnails.
         
     | 
| 
       5 
     | 
    
         
            -
            It  
     | 
| 
      
 5 
     | 
    
         
            +
            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.
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            Requirements:
         
     | 
| 
       10 
10 
     | 
    
         
             
            -------------
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ### OpenCV
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            If you want to use OpenCV on your own server, you need to install:
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       11 
16 
     | 
    
         
             
            - [OpenCV][opencv]
         
     | 
| 
       12 
17 
     | 
    
         
             
            - [OpenCV ruby binding][ruby-opencv]
         
     | 
| 
       13 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
            In case you get the error message `/ext/opencv/cverror.cpp:143: error: ‘CV_GpuCufftCallError’ was not declared in this scope` while installing the ruby binding,
         
     | 
| 
      
 20 
     | 
    
         
            +
            checkout the OpenCV_2.2 branch or just remove the line 143 from `/ext/opencv/cverror.cpp`
         
     | 
| 
      
 21 
     | 
    
         
            +
                      
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            ### Face.com
         
     | 
| 
      
 24 
     | 
    
         
            +
            - [rest-client][rest-client]
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            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.
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       14 
29 
     | 
    
         
             
            Installation:
         
     | 
| 
       15 
30 
     | 
    
         
             
            -------------
         
     | 
| 
       16 
31 
     | 
    
         
             
            - Add to your application `Gemfile`
         
     | 
| 
         @@ -21,39 +36,55 @@ Installation: 
     | 
|
| 
       21 
36 
     | 
    
         | 
| 
       22 
37 
     | 
    
         
             
                      bundle install
         
     | 
| 
       23 
38 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
            - Write an initializer setting the path of the haarcascade filters(`initializers/paperclip.rb` for example):   
         
     | 
| 
       25 
39 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
            - Write an initializer setting the detectors configuration (`initializers/paperclip.rb` for example):   
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            ### OpenCV
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            Set the path of the haarcascade filters:
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                      Paperclip::FaceCrop.detectors = {
         
     | 
| 
      
 47 
     | 
    
         
            +
                        'OpenCV' =>  { 
         
     | 
| 
      
 48 
     | 
    
         
            +
                          :face => %w(/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml)
         
     | 
| 
      
 49 
     | 
    
         
            +
                        }
         
     | 
| 
       28 
50 
     | 
    
         
             
                      }
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                      
         
     | 
| 
       29 
53 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
      
 54 
     | 
    
         
            +
            You can use more than one filter to try more accurate searches:
         
     | 
| 
       31 
55 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                      Paperclip::FaceCrop. 
     | 
| 
       33 
     | 
    
         
            -
                         
     | 
| 
       34 
     | 
    
         
            -
                           
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                         
     | 
| 
      
 56 
     | 
    
         
            +
                      Paperclip::FaceCrop.detectors = {
         
     | 
| 
      
 57 
     | 
    
         
            +
                        'OpenCV' =>  { 
         
     | 
| 
      
 58 
     | 
    
         
            +
                          :face => %w(/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml
         
     | 
| 
      
 59 
     | 
    
         
            +
                                      /usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml
         
     | 
| 
      
 60 
     | 
    
         
            +
                                      /usr/local/share/opencv/haarcascades/haarcascade_profileface.xml)
         
     | 
| 
      
 61 
     | 
    
         
            +
                        }
         
     | 
| 
       38 
62 
     | 
    
         
             
                      }
         
     | 
| 
       39 
63 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            In order to try to avoid some false positives, you can also specify other classifiers to detect other parts of the face. In that case, 
         
     | 
| 
      
 67 
     | 
    
         
            +
            only the found areas that contain parts like a mouth, an eye or a nose will be considered a face:
         
     | 
| 
       42 
68 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
                      Paperclip::FaceCrop. 
     | 
| 
       44 
     | 
    
         
            -
                         
     | 
| 
       45 
     | 
    
         
            -
                           
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                        ]
         
     | 
| 
      
 69 
     | 
    
         
            +
                      Paperclip::FaceCrop.detectors = {
         
     | 
| 
      
 70 
     | 
    
         
            +
                        'OpenCV' =>  { 
         
     | 
| 
      
 71 
     | 
    
         
            +
                          :face => %w(/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml
         
     | 
| 
      
 72 
     | 
    
         
            +
                                      /usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml
         
     | 
| 
      
 73 
     | 
    
         
            +
                                      /usr/local/share/opencv/haarcascades/haarcascade_profileface.xml),
         
     | 
| 
      
 74 
     | 
    
         
            +
                          
         
     | 
| 
      
 75 
     | 
    
         
            +
                          :parts => %w(/usr/local/share/opencv/haarcascades/haarcascade_mcs_nose.xml
         
     | 
| 
      
 76 
     | 
    
         
            +
                                       /usr/local/share/opencv/haarcascades/haarcascade_mcs_lefteye.xml
         
     | 
| 
      
 77 
     | 
    
         
            +
                                       /usr/local/share/opencv/haarcascades/haarcascade_mcs_righteye.xml)
         
     | 
| 
      
 78 
     | 
    
         
            +
                        }
         
     | 
| 
       54 
79 
     | 
    
         
             
                      }
         
     | 
| 
      
 80 
     | 
    
         
            +
                      
         
     | 
| 
       55 
81 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
      
 82 
     | 
    
         
            +
            ### Face.com
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                      Paperclip::FaceCrop.detectors = {
         
     | 
| 
      
 85 
     | 
    
         
            +
                        'FaceCom' => { :api_key => "<YOUR API KEY>", :api_secret => "<YOUR API SECRET>"}
         
     | 
| 
      
 86 
     | 
    
         
            +
                      }    
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
       57 
88 
     | 
    
         | 
| 
       58 
89 
     | 
    
         
             
            Usage:
         
     | 
| 
       59 
90 
     | 
    
         
             
            ------
         
     | 
| 
         @@ -71,15 +102,19 @@ In case no faces were found, it will behave simply as the `Paperclip::Thumbnail` 
     | 
|
| 
       71 
102 
     | 
    
         | 
| 
       72 
103 
     | 
    
         
             
            You can also set the debug mode to draw on the image the detected regions:
         
     | 
| 
       73 
104 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
                Paperclip::FaceCrop.debug =  
     | 
| 
      
 105 
     | 
    
         
            +
                Paperclip::FaceCrop.debug = Rails.env.development?
         
     | 
| 
       75 
106 
     | 
    
         | 
| 
       76 
107 
     | 
    
         
             
            
         
     | 
| 
       77 
108 
     | 
    
         | 
| 
      
 109 
     | 
    
         
            +
            Each detector will draw the found regions in different colors(Face.com detector in red and OpenCV in green)
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
       78 
111 
     | 
    
         
             
            Credits:
         
     | 
| 
       79 
112 
     | 
    
         
             
            --------
         
     | 
| 
       80 
113 
     | 
    
         
             
            Copyright (c) 2011 Borja Martín Sánchez de Vivar <borjamREMOVETHIS@dagi3d.net> - <http://dagi3d.net>, released under the MIT license  
         
     | 
| 
       81 
114 
     | 
    
         
             
            The photo used as example belongs to [Jesper Rønn-Jensen](http://www.flickr.com/photos/jesper/)
         
     | 
| 
       82 
115 
     | 
    
         | 
| 
      
 116 
     | 
    
         
            +
            [face_com]: http://face.com
         
     | 
| 
      
 117 
     | 
    
         
            +
            [rest-client]: https://rubygems.org/gems/rest-client
         
     | 
| 
       83 
118 
     | 
    
         
             
            [paperclip]: https://github.com/thoughtbot/paperclip
         
     | 
| 
       84 
119 
     | 
    
         
             
            [opencv]: http://opencv.willowgarage.com/
         
     | 
| 
       85 
120 
     | 
    
         
             
            [ruby-opencv]: https://github.com/ser1zw/ruby-opencv
         
     | 
    
        data/README_example_b.jpg
    CHANGED
    
    | 
         Binary file 
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rest_client'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class FaceCrop::Detector::FaceCom < FaceCrop::Detector::Base  
         
     | 
| 
      
 4 
     | 
    
         
            +
              URL = "http://api.face.com/faces/detect.json"
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
              def detect_faces(file)
         
     | 
| 
      
 7 
     | 
    
         
            +
                query = @options.to_query
         
     | 
| 
      
 8 
     | 
    
         
            +
                url = "#{URL}?#{query}"
         
     | 
| 
      
 9 
     | 
    
         
            +
                
         
     | 
| 
      
 10 
     | 
    
         
            +
                response = RestClient.post url, :file => File.new(file)
         
     | 
| 
      
 11 
     | 
    
         
            +
                response = JSON.parse(response)
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                photo = response['photos'].first
         
     | 
| 
      
 14 
     | 
    
         
            +
                photo['tags'].map do |tag|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # values are returned as percentual values
         
     | 
| 
      
 16 
     | 
    
         
            +
                  x = (photo['width'] * (tag['center']['x'] / 100.0)).to_i
         
     | 
| 
      
 17 
     | 
    
         
            +
                  y = (photo['height'] * (tag['center']['y'] / 100.0)).to_i
         
     | 
| 
      
 18 
     | 
    
         
            +
                  w = (photo['width'] * (tag['width'] / 100)).to_i  
         
     | 
| 
      
 19 
     | 
    
         
            +
                  h = (photo['height'] * (tag['height'] / 100)).to_i
         
     | 
| 
      
 20 
     | 
    
         
            +
                  
         
     | 
| 
      
 21 
     | 
    
         
            +
                  region = FaceCrop::Detector::Region.new(x, y, w, h)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  region.color = "green"
         
     | 
| 
      
 23 
     | 
    
         
            +
                  region
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'opencv'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require File.expand_path('../opencv_ext', __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class FaceCrop::Detector::OpenCV < FaceCrop::Detector::Base
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
              def detect_faces(file)
         
     | 
| 
      
 7 
     | 
    
         
            +
                
         
     | 
| 
      
 8 
     | 
    
         
            +
                image = OpenCV::IplImage.load(file, 1)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                faces_regions = detect_regions(image, @options[:face])
         
     | 
| 
      
 11 
     | 
    
         
            +
              
         
     | 
| 
      
 12 
     | 
    
         
            +
                #Paperclip::FaceCrop.classifiers[:nose]
         
     | 
| 
      
 13 
     | 
    
         
            +
                unless @options[:parts].nil?
         
     | 
| 
      
 14 
     | 
    
         
            +
                  faces_parts_regions = detect_regions(image, @options[:parts])
         
     | 
| 
      
 15 
     | 
    
         
            +
                
         
     | 
| 
      
 16 
     | 
    
         
            +
                  faces_regions.reject! do |face_region|
         
     | 
| 
      
 17 
     | 
    
         
            +
                    region = faces_parts_regions.detect do |part_region|
         
     | 
| 
      
 18 
     | 
    
         
            +
                      # part of a face can't be bigger than the face itself
         
     | 
| 
      
 19 
     | 
    
         
            +
                      face_region.collide?(part_region) && face_region > part_region
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  
         
     | 
| 
      
 22 
     | 
    
         
            +
                    region.nil?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                faces_regions
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
              
         
     | 
| 
      
 30 
     | 
    
         
            +
              private
         
     | 
| 
      
 31 
     | 
    
         
            +
              def detect_regions(image, classifiers, color = OpenCV::CvColor::Blue)
         
     | 
| 
      
 32 
     | 
    
         
            +
                regions = []
         
     | 
| 
      
 33 
     | 
    
         
            +
              
         
     | 
| 
      
 34 
     | 
    
         
            +
                classifiers.each do |classifier|
         
     | 
| 
      
 35 
     | 
    
         
            +
                  detector = OpenCV::CvHaarClassifierCascade::load(classifier)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  detector.detect_objects(image) do |region| 
         
     | 
| 
      
 37 
     | 
    
         
            +
                    region.color = "red"
         
     | 
| 
      
 38 
     | 
    
         
            +
                    regions << region 
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                regions
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/face_crop.rb
    ADDED
    
    | 
         @@ -0,0 +1,86 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module FaceCrop
         
     | 
| 
      
 2 
     | 
    
         
            +
                
         
     | 
| 
      
 3 
     | 
    
         
            +
              # Detector
         
     | 
| 
      
 4 
     | 
    
         
            +
              #
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Detector
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                autoload :FaceCom, File.expand_path('../detectors/face_com', __FILE__)
         
     | 
| 
      
 8 
     | 
    
         
            +
                autoload :OpenCV, File.expand_path('../detectors/opencv', __FILE__)
         
     | 
| 
      
 9 
     | 
    
         
            +
                
         
     | 
| 
      
 10 
     | 
    
         
            +
                # Base
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 13 
     | 
    
         
            +
                  
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # initialize
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def initialize(options)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @options = options
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # detect
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def detect(file)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    key = "#{self.class}#{file}"
         
     | 
| 
      
 24 
     | 
    
         
            +
                    regions = FaceCrop::Detector::Cache[key] 
         
     | 
| 
      
 25 
     | 
    
         
            +
                    return regions unless regions.nil?
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    regions = detect_faces(file)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    FaceCrop::Detector::Cache[key] = regions        
         
     | 
| 
      
 29 
     | 
    
         
            +
                    regions
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                class Cache
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @@cache = {}
         
     | 
| 
      
 36 
     | 
    
         
            +
                  
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def self.[]=(key, faces)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @@cache[key] = faces
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def self.[](key)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @@cache[key]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def self.clear
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @@cache = {}
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                
         
     | 
| 
      
 50 
     | 
    
         
            +
                # Region
         
     | 
| 
      
 51 
     | 
    
         
            +
                #
         
     | 
| 
      
 52 
     | 
    
         
            +
                class Region
         
     | 
| 
      
 53 
     | 
    
         
            +
                  attr_accessor :center
         
     | 
| 
      
 54 
     | 
    
         
            +
                  attr_accessor :width
         
     | 
| 
      
 55 
     | 
    
         
            +
                  attr_accessor :height      
         
     | 
| 
      
 56 
     | 
    
         
            +
                  attr_accessor :color  
         
     | 
| 
      
 57 
     | 
    
         
            +
                  
         
     | 
| 
      
 58 
     | 
    
         
            +
                  def initialize(x, y, width, height)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    @width, @height = width, height
         
     | 
| 
      
 60 
     | 
    
         
            +
                    @center = Point.new(x, y)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  
         
     | 
| 
      
 63 
     | 
    
         
            +
                  def top_left
         
     | 
| 
      
 64 
     | 
    
         
            +
                    @top_left ||= Point.new(@center.x - (width / 2), @center.y - (height / 2))
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  
         
     | 
| 
      
 67 
     | 
    
         
            +
                  def bottom_right
         
     | 
| 
      
 68 
     | 
    
         
            +
                    @bottom_right ||= Point.new(@center.x + (width / 2), @center.y + (height / 2))
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                  
         
     | 
| 
      
 71 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 72 
     | 
    
         
            +
                    "#{center.x},#{center.y}-#{width}x#{height}"
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
                
         
     | 
| 
      
 76 
     | 
    
         
            +
                # Point
         
     | 
| 
      
 77 
     | 
    
         
            +
                #
         
     | 
| 
      
 78 
     | 
    
         
            +
                class Point
         
     | 
| 
      
 79 
     | 
    
         
            +
                  attr_accessor :x
         
     | 
| 
      
 80 
     | 
    
         
            +
                  attr_accessor :y
         
     | 
| 
      
 81 
     | 
    
         
            +
                  def initialize(x,y)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @x, @y = x, y
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/paperclip-facecrop.rb
    CHANGED
    
    | 
         @@ -1,149 +1,2 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require File. 
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            module Paperclip
         
     | 
| 
       4 
     | 
    
         
            -
              class FaceCrop < Paperclip::Thumbnail
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
                @@debug = false
         
     | 
| 
       7 
     | 
    
         
            -
              
         
     | 
| 
       8 
     | 
    
         
            -
                cattr_accessor :classifiers
         
     | 
| 
       9 
     | 
    
         
            -
                cattr_accessor :debug
         
     | 
| 
       10 
     | 
    
         
            -
              
         
     | 
| 
       11 
     | 
    
         
            -
                def initialize(file, options = {}, attachment = nil)
         
     | 
| 
       12 
     | 
    
         
            -
                  super(file, options, attachment)
         
     | 
| 
       13 
     | 
    
         
            -
                
         
     | 
| 
       14 
     | 
    
         
            -
                  faces_regions = []
         
     | 
| 
       15 
     | 
    
         
            -
                  faces_parts_regions = []
         
     | 
| 
       16 
     | 
    
         
            -
                    
         
     | 
| 
       17 
     | 
    
         
            -
                  raise "No classifiers were defined" if self.classifiers.nil?
         
     | 
| 
       18 
     | 
    
         
            -
                
         
     | 
| 
       19 
     | 
    
         
            -
                  image = OpenCV::IplImage.load(file.path, 1)
         
     | 
| 
       20 
     | 
    
         
            -
                
         
     | 
| 
       21 
     | 
    
         
            -
                  faces_regions = detect_regions(image, self.classifiers[:face])
         
     | 
| 
       22 
     | 
    
         
            -
                
         
     | 
| 
       23 
     | 
    
         
            -
                  #Paperclip::FaceCrop.classifiers[:nose]
         
     | 
| 
       24 
     | 
    
         
            -
                  unless self.classifiers[:parts].nil?
         
     | 
| 
       25 
     | 
    
         
            -
                    faces_parts_regions = detect_regions(image, self.classifiers[:parts], OpenCV::CvColor::Red)
         
     | 
| 
       26 
     | 
    
         
            -
                  
         
     | 
| 
       27 
     | 
    
         
            -
                    faces_regions.reject! do |face_region|
         
     | 
| 
       28 
     | 
    
         
            -
                      region = faces_parts_regions.detect do |part_region|
         
     | 
| 
       29 
     | 
    
         
            -
                        # part of a face can't be bigger than the face itself
         
     | 
| 
       30 
     | 
    
         
            -
                        face_region.collide?(part_region) && face_region > part_region
         
     | 
| 
       31 
     | 
    
         
            -
                      end
         
     | 
| 
       32 
     | 
    
         
            -
                    
         
     | 
| 
       33 
     | 
    
         
            -
                      region.nil?
         
     | 
| 
       34 
     | 
    
         
            -
                    end
         
     | 
| 
       35 
     | 
    
         
            -
                  end
         
     | 
| 
       36 
     | 
    
         
            -
                
         
     | 
| 
       37 
     | 
    
         
            -
                  x_coords = []
         
     | 
| 
       38 
     | 
    
         
            -
                  y_coords = []
         
     | 
| 
       39 
     | 
    
         
            -
                  widths   = []
         
     | 
| 
       40 
     | 
    
         
            -
                  heights  = []
         
     | 
| 
       41 
     | 
    
         
            -
                
         
     | 
| 
       42 
     | 
    
         
            -
                  faces_regions.each do |region|
         
     | 
| 
       43 
     | 
    
         
            -
                    x_coords << region.top_left.x << region.bottom_right.x
         
     | 
| 
       44 
     | 
    
         
            -
                    y_coords << region.top_left.y << region.bottom_right.y
         
     | 
| 
       45 
     | 
    
         
            -
                    widths << region.width
         
     | 
| 
       46 
     | 
    
         
            -
                    heights << region.height
         
     | 
| 
       47 
     | 
    
         
            -
                  end
         
     | 
| 
       48 
     | 
    
         
            -
                
         
     | 
| 
       49 
     | 
    
         
            -
                  @has_faces = faces_regions.size > 0
         
     | 
| 
       50 
     | 
    
         
            -
                 
         
     | 
| 
       51 
     | 
    
         
            -
                  if @has_faces
         
     | 
| 
       52 
     | 
    
         
            -
                    @top_left_x = x_coords.min
         
     | 
| 
       53 
     | 
    
         
            -
                    @top_left_y = y_coords.min
         
     | 
| 
       54 
     | 
    
         
            -
                    @bottom_right_x = x_coords.max
         
     | 
| 
       55 
     | 
    
         
            -
                    @bottom_right_y = y_coords.max
         
     | 
| 
       56 
     | 
    
         
            -
                  
         
     | 
| 
       57 
     | 
    
         
            -
                    # average faces areas
         
     | 
| 
       58 
     | 
    
         
            -
                    average_face_width  = widths.sum / widths.size
         
     | 
| 
       59 
     | 
    
         
            -
                    average_face_height = heights.sum / heights.size
         
     | 
| 
       60 
     | 
    
         
            -
                  
         
     | 
| 
       61 
     | 
    
         
            -
                    # calculating the surrounding margin of the area that covers all the found faces
         
     | 
| 
       62 
     | 
    
         
            -
                    #
         
     | 
| 
       63 
     | 
    
         
            -
                  
         
     | 
| 
       64 
     | 
    
         
            -
                    # new width
         
     | 
| 
       65 
     | 
    
         
            -
                    @top_left_x -= average_face_width / 2
         
     | 
| 
       66 
     | 
    
         
            -
                    @bottom_right_x += average_face_width / 2
         
     | 
| 
       67 
     | 
    
         
            -
                  
         
     | 
| 
       68 
     | 
    
         
            -
                    # new height
         
     | 
| 
       69 
     | 
    
         
            -
                    @top_left_y -= average_face_height / 2
         
     | 
| 
       70 
     | 
    
         
            -
                    @bottom_right_y += average_face_height / 1.6
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                    calculate_bounds
         
     | 
| 
       73 
     | 
    
         
            -
                  
         
     | 
| 
       74 
     | 
    
         
            -
                    # if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled
         
     | 
| 
       75 
     | 
    
         
            -
                    #
         
     | 
| 
       76 
     | 
    
         
            -
                    if @faces_width < @target_geometry.width 
         
     | 
| 
       77 
     | 
    
         
            -
                      delta_width = (@target_geometry.width - @faces_width) / 2
         
     | 
| 
       78 
     | 
    
         
            -
                      @top_left_x -= delta_width
         
     | 
| 
       79 
     | 
    
         
            -
                      @bottom_right_x += delta_width
         
     | 
| 
       80 
     | 
    
         
            -
                      calculate_bounds
         
     | 
| 
       81 
     | 
    
         
            -
                    end
         
     | 
| 
       82 
     | 
    
         
            -
                  
         
     | 
| 
       83 
     | 
    
         
            -
                    #raise (@target_geometry.height > 0 and @faces_height < @target_geometry.height).to_s
         
     | 
| 
       84 
     | 
    
         
            -
                  
         
     | 
| 
       85 
     | 
    
         
            -
                    if (@target_geometry.height > 0 and @faces_height < @target_geometry.height)
         
     | 
| 
       86 
     | 
    
         
            -
                      delta_height = (@target_geometry.height - @faces_height) / 2
         
     | 
| 
       87 
     | 
    
         
            -
                      @top_left_y -= delta_height
         
     | 
| 
       88 
     | 
    
         
            -
                      @bottom_right_y += delta_height
         
     | 
| 
       89 
     | 
    
         
            -
                      calculate_bounds
         
     | 
| 
       90 
     | 
    
         
            -
                    end
         
     | 
| 
       91 
     | 
    
         
            -
                  
         
     | 
| 
       92 
     | 
    
         
            -
                    @faces_height = @faces_width if @target_geometry.height == 0
         
     | 
| 
       93 
     | 
    
         
            -
                  
         
     | 
| 
       94 
     | 
    
         
            -
                    @current_geometry = Paperclip::Geometry.new(@faces_width, @faces_height)
         
     | 
| 
       95 
     | 
    
         
            -
                  end
         
     | 
| 
       96 
     | 
    
         
            -
                
         
     | 
| 
       97 
     | 
    
         
            -
                end
         
     | 
| 
       98 
     | 
    
         
            -
              
         
     | 
| 
       99 
     | 
    
         
            -
              
         
     | 
| 
       100 
     | 
    
         
            -
                def transformation_command
         
     | 
| 
       101 
     | 
    
         
            -
                  return super unless @has_faces
         
     | 
| 
       102 
     | 
    
         
            -
                
         
     | 
| 
       103 
     | 
    
         
            -
                  scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
         
     | 
| 
       104 
     | 
    
         
            -
                  faces_crop = "%dx%d+%d+%d" % [@faces_width, @faces_height, @top_left_x, @top_left_y]
         
     | 
| 
       105 
     | 
    
         
            -
                
         
     | 
| 
       106 
     | 
    
         
            -
                  trans = []
         
     | 
| 
       107 
     | 
    
         
            -
                  trans << "-crop" << %["#{faces_crop}"] << "+repage"
         
     | 
| 
       108 
     | 
    
         
            -
                  trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
         
     | 
| 
       109 
     | 
    
         
            -
                  trans << "-crop" << %["#{crop}"] << "+repage" if crop
         
     | 
| 
       110 
     | 
    
         
            -
                  trans
         
     | 
| 
       111 
     | 
    
         
            -
                end
         
     | 
| 
       112 
     | 
    
         
            -
              
         
     | 
| 
       113 
     | 
    
         
            -
                private
         
     | 
| 
       114 
     | 
    
         
            -
              
         
     | 
| 
       115 
     | 
    
         
            -
                # calculate_bounds
         
     | 
| 
       116 
     | 
    
         
            -
                #
         
     | 
| 
       117 
     | 
    
         
            -
                def calculate_bounds
         
     | 
| 
       118 
     | 
    
         
            -
                  @top_left_x = 0 if @top_left_x < 0      
         
     | 
| 
       119 
     | 
    
         
            -
                  @bottom_right_x = @current_geometry.width if @bottom_right_x > @current_geometry.width
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                  @top_left_y = 0 if @top_left_y < 0
         
     | 
| 
       122 
     | 
    
         
            -
                  @bottom_right_y = @current_geometry.height if @bottom_right_y > @current_geometry.height
         
     | 
| 
       123 
     | 
    
         
            -
                
         
     | 
| 
       124 
     | 
    
         
            -
                  @faces_width = @bottom_right_x - @top_left_x
         
     | 
| 
       125 
     | 
    
         
            -
                  @faces_height = @bottom_right_y - @top_left_y
         
     | 
| 
       126 
     | 
    
         
            -
                end
         
     | 
| 
       127 
     | 
    
         
            -
              
         
     | 
| 
       128 
     | 
    
         
            -
                # detect_regions
         
     | 
| 
       129 
     | 
    
         
            -
                #
         
     | 
| 
       130 
     | 
    
         
            -
                def detect_regions(image, classifiers, color = OpenCV::CvColor::Blue)
         
     | 
| 
       131 
     | 
    
         
            -
                  regions = []
         
     | 
| 
       132 
     | 
    
         
            -
                
         
     | 
| 
       133 
     | 
    
         
            -
                  classifiers.each do |classifier|
         
     | 
| 
       134 
     | 
    
         
            -
                    detector = OpenCV::CvHaarClassifierCascade::load(classifier)
         
     | 
| 
       135 
     | 
    
         
            -
                    detector.detect_objects(image) do |region| 
         
     | 
| 
       136 
     | 
    
         
            -
                      regions << region 
         
     | 
| 
       137 
     | 
    
         
            -
                      image.rectangle!(region.top_left, region.bottom_right, :color => color) if self.debug 
         
     | 
| 
       138 
     | 
    
         
            -
                    end
         
     | 
| 
       139 
     | 
    
         
            -
                  end
         
     | 
| 
       140 
     | 
    
         
            -
                
         
     | 
| 
       141 
     | 
    
         
            -
                  if self.debug 
         
     | 
| 
       142 
     | 
    
         
            -
                    image.save_image(@file.path)
         
     | 
| 
       143 
     | 
    
         
            -
                    Rails.logger.info(regions)
         
     | 
| 
       144 
     | 
    
         
            -
                  end
         
     | 
| 
       145 
     | 
    
         
            -
                
         
     | 
| 
       146 
     | 
    
         
            -
                  regions
         
     | 
| 
       147 
     | 
    
         
            -
                end
         
     | 
| 
       148 
     | 
    
         
            -
              end
         
     | 
| 
       149 
     | 
    
         
            -
            end
         
     | 
| 
      
 1 
     | 
    
         
            +
            require File.expand_path('../processor', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
            require File.expand_path('../railtie', __FILE__)
         
     | 
    
        data/lib/processor.rb
    ADDED
    
    | 
         @@ -0,0 +1,143 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path('../face_crop', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Paperclip
         
     | 
| 
      
 4 
     | 
    
         
            +
              class FaceCrop < Paperclip::Thumbnail
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                @@debug = false
         
     | 
| 
      
 7 
     | 
    
         
            +
              
         
     | 
| 
      
 8 
     | 
    
         
            +
                #cattr_accessor :classifiers
         
     | 
| 
      
 9 
     | 
    
         
            +
                cattr_accessor :debug
         
     | 
| 
      
 10 
     | 
    
         
            +
                    
         
     | 
| 
      
 11 
     | 
    
         
            +
                def self.detectors=(detectors)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @@detectors = detectors.map do |name, options|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    #require File.expand_path("../detectors/#{name}", __FILE__)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    detector_class = "FaceCrop::Detector::#{name}".constantize
         
     | 
| 
      
 15 
     | 
    
         
            +
                    detector = detector_class.new(options)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              
         
     | 
| 
      
 19 
     | 
    
         
            +
                def initialize(file, options = {}, attachment = nil)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  super(file, options, attachment)
         
     | 
| 
      
 21 
     | 
    
         
            +
                
         
     | 
| 
      
 22 
     | 
    
         
            +
                  raise "No detectors were defined" if @@detectors.nil?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  
         
     | 
| 
      
 24 
     | 
    
         
            +
                  faces_regions = []
         
     | 
| 
      
 25 
     | 
    
         
            +
                  faces_parts_regions = []
         
     | 
| 
      
 26 
     | 
    
         
            +
                  
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @@detectors.each do |detector|
         
     | 
| 
      
 28 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 29 
     | 
    
         
            +
                      faces_regions += detector.detect(file.path)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    rescue Exception => e
         
     | 
| 
      
 31 
     | 
    
         
            +
                      Rails.logger.error(e)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  
         
     | 
| 
      
 35 
     | 
    
         
            +
                  x_coords, y_coords, widths, heights = [], [], [], []
         
     | 
| 
      
 36 
     | 
    
         
            +
                
         
     | 
| 
      
 37 
     | 
    
         
            +
                  faces_regions.each do |region|
         
     | 
| 
      
 38 
     | 
    
         
            +
                    x_coords << region.top_left.x << region.bottom_right.x
         
     | 
| 
      
 39 
     | 
    
         
            +
                    y_coords << region.top_left.y << region.bottom_right.y
         
     | 
| 
      
 40 
     | 
    
         
            +
                    widths << region.width
         
     | 
| 
      
 41 
     | 
    
         
            +
                    heights << region.height
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                      
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @has_faces = faces_regions.size > 0
         
     | 
| 
      
 45 
     | 
    
         
            +
                 
         
     | 
| 
      
 46 
     | 
    
         
            +
                  if @has_faces
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @top_left_x = x_coords.min
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @top_left_y = y_coords.min
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @bottom_right_x = x_coords.max
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @bottom_right_y = y_coords.max
         
     | 
| 
      
 51 
     | 
    
         
            +
                    
         
     | 
| 
      
 52 
     | 
    
         
            +
                    
         
     | 
| 
      
 53 
     | 
    
         
            +
                    #puts @top_left_x.to_s
         
     | 
| 
      
 54 
     | 
    
         
            +
                    
         
     | 
| 
      
 55 
     | 
    
         
            +
                    # average faces areas
         
     | 
| 
      
 56 
     | 
    
         
            +
                    average_face_width  = widths.sum / widths.size
         
     | 
| 
      
 57 
     | 
    
         
            +
                    average_face_height = heights.sum / heights.size
         
     | 
| 
      
 58 
     | 
    
         
            +
                  
         
     | 
| 
      
 59 
     | 
    
         
            +
                    # calculating the surrounding margin of the area that covers all the found faces
         
     | 
| 
      
 60 
     | 
    
         
            +
                    #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  
         
     | 
| 
      
 62 
     | 
    
         
            +
                    # new width
         
     | 
| 
      
 63 
     | 
    
         
            +
                    @top_left_x -= average_face_width / 1.2
         
     | 
| 
      
 64 
     | 
    
         
            +
                    @bottom_right_x += average_face_width / 1.2
         
     | 
| 
      
 65 
     | 
    
         
            +
                  
         
     | 
| 
      
 66 
     | 
    
         
            +
                    # new height
         
     | 
| 
      
 67 
     | 
    
         
            +
                    #puts ":::#{@top_left_x}---#{average_face_width}"
         
     | 
| 
      
 68 
     | 
    
         
            +
                    #return
         
     | 
| 
      
 69 
     | 
    
         
            +
                    
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @top_left_y -= average_face_height / 1.2
         
     | 
| 
      
 71 
     | 
    
         
            +
                    @bottom_right_y += average_face_height / 1.6
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    calculate_bounds
         
     | 
| 
      
 74 
     | 
    
         
            +
                  
         
     | 
| 
      
 75 
     | 
    
         
            +
                    # if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled
         
     | 
| 
      
 76 
     | 
    
         
            +
                    #
         
     | 
| 
      
 77 
     | 
    
         
            +
                    if @faces_width < @target_geometry.width 
         
     | 
| 
      
 78 
     | 
    
         
            +
                      delta_width = (@target_geometry.width - @faces_width) / 2
         
     | 
| 
      
 79 
     | 
    
         
            +
                      @top_left_x -= delta_width
         
     | 
| 
      
 80 
     | 
    
         
            +
                      @bottom_right_x += delta_width
         
     | 
| 
      
 81 
     | 
    
         
            +
                      calculate_bounds
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  
         
     | 
| 
      
 84 
     | 
    
         
            +
                    #raise (@target_geometry.height > 0 and @faces_height < @target_geometry.height).to_s
         
     | 
| 
      
 85 
     | 
    
         
            +
                  
         
     | 
| 
      
 86 
     | 
    
         
            +
                    if (@target_geometry.height > 0 and @faces_height < @target_geometry.height)
         
     | 
| 
      
 87 
     | 
    
         
            +
                      delta_height = (@target_geometry.height - @faces_height) / 2
         
     | 
| 
      
 88 
     | 
    
         
            +
                      @top_left_y -= delta_height
         
     | 
| 
      
 89 
     | 
    
         
            +
                      @bottom_right_y += delta_height
         
     | 
| 
      
 90 
     | 
    
         
            +
                      calculate_bounds
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  
         
     | 
| 
      
 93 
     | 
    
         
            +
                    @faces_height = @faces_width if @target_geometry.height == 0
         
     | 
| 
      
 94 
     | 
    
         
            +
                  
         
     | 
| 
      
 95 
     | 
    
         
            +
                    @current_geometry = Paperclip::Geometry.new(@faces_width, @faces_height)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    
         
     | 
| 
      
 97 
     | 
    
         
            +
                    if @@debug
         
     | 
| 
      
 98 
     | 
    
         
            +
                      parameters = []
         
     | 
| 
      
 99 
     | 
    
         
            +
                      parameters << "-stroke" << "green"
         
     | 
| 
      
 100 
     | 
    
         
            +
                      parameters << "-fill" << "none"
         
     | 
| 
      
 101 
     | 
    
         
            +
                      parameters << faces_regions.map {|r| "-stroke #{r.color} -draw 'rectangle #{r.top_left.x},#{r.top_left.y} #{r.bottom_right.x},#{r.bottom_right.y}'"}
         
     | 
| 
      
 102 
     | 
    
         
            +
                      parameters << ":source"
         
     | 
| 
      
 103 
     | 
    
         
            +
                      parameters << ":dest"
         
     | 
| 
      
 104 
     | 
    
         
            +
                      parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
         
     | 
| 
      
 105 
     | 
    
         
            +
                                
         
     | 
| 
      
 106 
     | 
    
         
            +
                      Paperclip.run("convert", parameters, :source => "#{File.expand_path(file.path)}", :dest => "#{File.expand_path(file.path)}")
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    
         
     | 
| 
      
 109 
     | 
    
         
            +
                    
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
              
         
     | 
| 
      
 113 
     | 
    
         
            +
              
         
     | 
| 
      
 114 
     | 
    
         
            +
                def transformation_command
         
     | 
| 
      
 115 
     | 
    
         
            +
                  return super unless @has_faces
         
     | 
| 
      
 116 
     | 
    
         
            +
                
         
     | 
| 
      
 117 
     | 
    
         
            +
                  scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  faces_crop = "%dx%d+%d+%d" % [@faces_width, @faces_height, @top_left_x, @top_left_y]
         
     | 
| 
      
 119 
     | 
    
         
            +
                
         
     | 
| 
      
 120 
     | 
    
         
            +
                  trans = []
         
     | 
| 
      
 121 
     | 
    
         
            +
                  trans << "-crop" << %["#{faces_crop}"] << "+repage"
         
     | 
| 
      
 122 
     | 
    
         
            +
                  trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
         
     | 
| 
      
 123 
     | 
    
         
            +
                  trans << "-crop" << %["#{crop}"] << "+repage" if crop
         
     | 
| 
      
 124 
     | 
    
         
            +
                  trans
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
              
         
     | 
| 
      
 127 
     | 
    
         
            +
                private
         
     | 
| 
      
 128 
     | 
    
         
            +
              
         
     | 
| 
      
 129 
     | 
    
         
            +
                # calculate_bounds
         
     | 
| 
      
 130 
     | 
    
         
            +
                #
         
     | 
| 
      
 131 
     | 
    
         
            +
                def calculate_bounds
         
     | 
| 
      
 132 
     | 
    
         
            +
                  @top_left_x = 0 if @top_left_x < 0      
         
     | 
| 
      
 133 
     | 
    
         
            +
                  @bottom_right_x = @current_geometry.width if @bottom_right_x > @current_geometry.width
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  @top_left_y = 0 if @top_left_y < 0
         
     | 
| 
      
 136 
     | 
    
         
            +
                  @bottom_right_y = @current_geometry.height if @bottom_right_y > @current_geometry.height
         
     | 
| 
      
 137 
     | 
    
         
            +
                
         
     | 
| 
      
 138 
     | 
    
         
            +
                  @faces_width = @bottom_right_x - @top_left_x
         
     | 
| 
      
 139 
     | 
    
         
            +
                  @faces_height = @bottom_right_y - @top_left_y
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
              
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/railtie.rb
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module FaceCrop
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Railtie < Rails::Railtie
         
     | 
| 
      
 3 
     | 
    
         
            +
                initializer "paperclip-facecrop.extend_has_attachment" do
         
     | 
| 
      
 4 
     | 
    
         
            +
                  raise "Paperclip needed" unless defined?(Paperclip)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  ActiveSupport.on_load :active_record do
         
     | 
| 
      
 6 
     | 
    
         
            +
                    
         
     | 
| 
      
 7 
     | 
    
         
            +
                    class ActiveRecord::Base
         
     | 
| 
      
 8 
     | 
    
         
            +
                      
         
     | 
| 
      
 9 
     | 
    
         
            +
                      class << self
         
     | 
| 
      
 10 
     | 
    
         
            +
                        def has_attached_file_with_face_crop_cache(name, args)
         
     | 
| 
      
 11 
     | 
    
         
            +
                          has_attached_file_without_face_crop_cache(name, args)
         
     | 
| 
      
 12 
     | 
    
         
            +
                          send("after_#{name}_post_process", lambda { FaceCrop::Detector::Cache.clear })
         
     | 
| 
      
 13 
     | 
    
         
            +
                        end
         
     | 
| 
      
 14 
     | 
    
         
            +
                        
         
     | 
| 
      
 15 
     | 
    
         
            +
                        alias_method_chain :has_attached_file, :face_crop_cache
         
     | 
| 
      
 16 
     | 
    
         
            +
                      end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
    
        data/paperclip-facecrop.gemspec
    CHANGED
    
    | 
         @@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__) 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            Gem::Specification.new do |s|
         
     | 
| 
       5 
5 
     | 
    
         
             
              s.name = "paperclip-facecrop"
         
     | 
| 
       6 
     | 
    
         
            -
              s.version = "0.0 
     | 
| 
      
 6 
     | 
    
         
            +
              s.version = "0.1.0"
         
     | 
| 
       7 
7 
     | 
    
         
             
              s.authors = ["Borja Martín"]
         
     | 
| 
       8 
8 
     | 
    
         
             
              s.description = %q{Paperclip processor that is aware of the faces detected on the image so that they don't get cropped or aren't shown too small while generating the thumbnails}
         
     | 
| 
       9 
9 
     | 
    
         
             
              s.summary = %q{Paperclip processor that is aware of the faces found on the image}
         
     | 
| 
         @@ -13,5 +13,4 @@ Gem::Specification.new do |s| 
     | 
|
| 
       13 
13 
     | 
    
         
             
              s.files = `git ls-files`.split("\n")
         
     | 
| 
       14 
14 
     | 
    
         
             
              s.has_rdoc = false
         
     | 
| 
       15 
15 
     | 
    
         
             
              s.add_runtime_dependency("paperclip")
         
     | 
| 
       16 
     | 
    
         
            -
              s.add_runtime_dependency("opencv", "= 0.0.6")
         
     | 
| 
       17 
16 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,99 +1,70 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            --- !ruby/object:Gem::Specification 
     | 
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: paperclip-facecrop
         
     | 
| 
       3 
     | 
    
         
            -
            version: !ruby/object:Gem::Version 
     | 
| 
       4 
     | 
    
         
            -
               
     | 
| 
       5 
     | 
    
         
            -
               
     | 
| 
       6 
     | 
    
         
            -
              - 0
         
     | 
| 
       7 
     | 
    
         
            -
              - 0
         
     | 
| 
       8 
     | 
    
         
            -
              - 6
         
     | 
| 
       9 
     | 
    
         
            -
              version: 0.0.6
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
      
 5 
     | 
    
         
            +
              prerelease: 
         
     | 
| 
       10 
6 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       11 
     | 
    
         
            -
            authors: 
     | 
| 
       12 
     | 
    
         
            -
            -  
     | 
| 
      
 7 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Borja Martín
         
     | 
| 
       13 
9 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       14 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       15 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
            dependencies: 
         
     | 
| 
       20 
     | 
    
         
            -
            - !ruby/object:Gem::Dependency 
         
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2011-08-14 00:00:00.000000000Z
         
     | 
| 
      
 13 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 14 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
       21 
15 
     | 
    
         
             
              name: paperclip
         
     | 
| 
       22 
     | 
    
         
            -
               
     | 
| 
       23 
     | 
    
         
            -
              requirement: &id001 !ruby/object:Gem::Requirement 
         
     | 
| 
      
 16 
     | 
    
         
            +
              requirement: &2153665720 !ruby/object:Gem::Requirement
         
     | 
| 
       24 
17 
     | 
    
         
             
                none: false
         
     | 
| 
       25 
     | 
    
         
            -
                requirements: 
     | 
| 
       26 
     | 
    
         
            -
                - -  
     | 
| 
       27 
     | 
    
         
            -
                  - !ruby/object:Gem::Version 
     | 
| 
       28 
     | 
    
         
            -
                     
     | 
| 
       29 
     | 
    
         
            -
                    - 0
         
     | 
| 
       30 
     | 
    
         
            -
                    version: "0"
         
     | 
| 
      
 18 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 19 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 20 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 21 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
       31 
22 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       32 
     | 
    
         
            -
              version_requirements: *id001
         
     | 
| 
       33 
     | 
    
         
            -
            - !ruby/object:Gem::Dependency 
         
     | 
| 
       34 
     | 
    
         
            -
              name: opencv
         
     | 
| 
       35 
23 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       36 
     | 
    
         
            -
               
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                - - "="
         
     | 
| 
       40 
     | 
    
         
            -
                  - !ruby/object:Gem::Version 
         
     | 
| 
       41 
     | 
    
         
            -
                    segments: 
         
     | 
| 
       42 
     | 
    
         
            -
                    - 0
         
     | 
| 
       43 
     | 
    
         
            -
                    - 0
         
     | 
| 
       44 
     | 
    
         
            -
                    - 6
         
     | 
| 
       45 
     | 
    
         
            -
                    version: 0.0.6
         
     | 
| 
       46 
     | 
    
         
            -
              type: :runtime
         
     | 
| 
       47 
     | 
    
         
            -
              version_requirements: *id002
         
     | 
| 
       48 
     | 
    
         
            -
            description: Paperclip processor that is aware of the faces detected on the image so that they don't get cropped or aren't shown too small while generating the thumbnails
         
     | 
| 
      
 24 
     | 
    
         
            +
              version_requirements: *2153665720
         
     | 
| 
      
 25 
     | 
    
         
            +
            description: Paperclip processor that is aware of the faces detected on the image
         
     | 
| 
      
 26 
     | 
    
         
            +
              so that they don't get cropped or aren't shown too small while generating the thumbnails
         
     | 
| 
       49 
27 
     | 
    
         
             
            email: borjam@dagi3d.net
         
     | 
| 
       50 
28 
     | 
    
         
             
            executables: []
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
29 
     | 
    
         
             
            extensions: []
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
30 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
            files: 
         
     | 
| 
      
 31 
     | 
    
         
            +
            files:
         
     | 
| 
       57 
32 
     | 
    
         
             
            - .gitignore
         
     | 
| 
       58 
33 
     | 
    
         
             
            - Gemfile
         
     | 
| 
       59 
34 
     | 
    
         
             
            - README.md
         
     | 
| 
       60 
35 
     | 
    
         
             
            - README_example.jpg
         
     | 
| 
       61 
36 
     | 
    
         
             
            - README_example_b.jpg
         
     | 
| 
       62 
37 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       63 
     | 
    
         
            -
            - lib/ 
     | 
| 
      
 38 
     | 
    
         
            +
            - lib/detectors/face_com.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            - lib/detectors/opencv.rb
         
     | 
| 
      
 40 
     | 
    
         
            +
            - lib/detectors/opencv_ext.rb
         
     | 
| 
      
 41 
     | 
    
         
            +
            - lib/face_crop.rb
         
     | 
| 
       64 
42 
     | 
    
         
             
            - lib/paperclip-facecrop.rb
         
     | 
| 
      
 43 
     | 
    
         
            +
            - lib/processor.rb
         
     | 
| 
      
 44 
     | 
    
         
            +
            - lib/railtie.rb
         
     | 
| 
       65 
45 
     | 
    
         
             
            - paperclip-facecrop.gemspec
         
     | 
| 
       66 
     | 
    
         
            -
            has_rdoc: true
         
     | 
| 
       67 
46 
     | 
    
         
             
            homepage: http://github.com/dagi3d/paperclip-facecrop
         
     | 
| 
       68 
47 
     | 
    
         
             
            licenses: []
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
48 
     | 
    
         
             
            post_install_message: 
         
     | 
| 
       71 
49 
     | 
    
         
             
            rdoc_options: []
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
            require_paths: 
         
     | 
| 
      
 50 
     | 
    
         
            +
            require_paths:
         
     | 
| 
       74 
51 
     | 
    
         
             
            - lib
         
     | 
| 
       75 
     | 
    
         
            -
            required_ruby_version: !ruby/object:Gem::Requirement 
     | 
| 
      
 52 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
       76 
53 
     | 
    
         
             
              none: false
         
     | 
| 
       77 
     | 
    
         
            -
              requirements: 
     | 
| 
       78 
     | 
    
         
            -
              - -  
     | 
| 
       79 
     | 
    
         
            -
                - !ruby/object:Gem::Version 
     | 
| 
       80 
     | 
    
         
            -
                   
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                  version: "0"
         
     | 
| 
       83 
     | 
    
         
            -
            required_rubygems_version: !ruby/object:Gem::Requirement 
         
     | 
| 
      
 54 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 55 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 56 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 57 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 58 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
       84 
59 
     | 
    
         
             
              none: false
         
     | 
| 
       85 
     | 
    
         
            -
              requirements: 
     | 
| 
       86 
     | 
    
         
            -
              - -  
     | 
| 
       87 
     | 
    
         
            -
                - !ruby/object:Gem::Version 
     | 
| 
       88 
     | 
    
         
            -
                   
     | 
| 
       89 
     | 
    
         
            -
                  - 0
         
     | 
| 
       90 
     | 
    
         
            -
                  version: "0"
         
     | 
| 
      
 60 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 61 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 62 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 63 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
       91 
64 
     | 
    
         
             
            requirements: []
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
65 
     | 
    
         
             
            rubyforge_project: 
         
     | 
| 
       94 
     | 
    
         
            -
            rubygems_version: 1. 
     | 
| 
      
 66 
     | 
    
         
            +
            rubygems_version: 1.8.6
         
     | 
| 
       95 
67 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       96 
68 
     | 
    
         
             
            specification_version: 3
         
     | 
| 
       97 
69 
     | 
    
         
             
            summary: Paperclip processor that is aware of the faces found on the image
         
     | 
| 
       98 
70 
     | 
    
         
             
            test_files: []
         
     | 
| 
       99 
     | 
    
         
            -
             
     |