Smartcropper for Ruby

Content aware cropping for Ruby and Carrierwave

View the Project on GitHub berkes/smartcropper

Smartcropper

Crops images based on entropy: leaving the most interesting part intact.

Best results achieved in combination with scaling: the cropping is then only used to square the image, cutting off the least interesting part. The trimming simply chops off te edge that is least interesting, and continues doing so, untill it reached the requested size.

Usage

gem install smartcropper
Or add to you bundle file.

With carrierwave, you use it in a custom manipulate! block. For example, carrierwave in a Rails project:

File uploaders/attachement_uploader.rb:

def smart_crop_and_scale(width, height)
  manipulate! do |img|
    img = CropToelie.new(img)
    img = img.smart_crop_and_scale(width, height)
    img = yield(img) if block_given?
    img
  end
end

# Create different versions of your uploaded files:
# A square and tiny thumbnail
version :thumb do
  process :smart_crop_and_scale => [80, 80]
end
# A large version, only cropped
version :preview do
  processs :crop => [200,400]
end
# A square version
version :cover do
  process :smart_square
end

With custom code, you simply create an instance of SmartCropper


image = Magick::ImageList("carice.jpg")
SmartCropper.new(image).smart_square.write("carice_square.jpg")

# Or, simpler
SmartCropper.from_file("carice.jpg").smart_square.write("carice_square.jpg")

Examples

Below some examples, generated with this script.
original
original
smart_crop 400, 300
smart_crop 400, 300
smart_crop_and_scale 400, 300
smart_crop_and_scale 400, 300
smart_square
smart_square
original
original
smart_crop 400, 300
smart_crop 400, 300
smart_crop_and_scale 400, 300
smart_crop_and_scale 400, 300
smart_square
smart_square
original
original
smart_crop 400, 300
smart_crop 400, 300
smart_crop_and_scale 400, 300
smart_crop_and_scale 400, 300
smart_square
smart_square
original
original
smart_crop 400, 300
smart_crop 400, 300
smart_crop_and_scale 400, 300
smart_crop_and_scale 400, 300
smart_square
smart_square

Workings

Smartcropper grabs a slice from both outer sides of the image. It compares the entropy of the slices and then removes the slice with the lowest entropy. More differentiating pixels (colors, shadows, depth) indicates a more interesting part of the image; a higher entropy.

Vertical map of the entropy
entropy map vertical

It then repeats that for the top-and bottom

Horizontal map of the entropy
entropy map horizontal

As you can see, entropy of a slice is not a perfect measurement of the "interestingness". Some slices have a high entropy, not because they contain the focus of the image, but some other artifact, like a tree, a logo or a lot of reflecting water.

Attempts to scan the image, rather then shaving it, have been made. But the result is not much better, yet the performance is hundreds times worse.

Mapping the entropy
entropy map

For now, shaving suffices. But I want to re-investigate the scanning-method, combined with some simple statistics to determine hotspots in the image. Then the "hotness" and size of these hotspots can be used to determine the boundries of the "interesting part".
The biggest hurdle, then, is to reduce memory-usage and amount of iterations; since the value of each pixel in an image must be loaded into memory and looped over, multiple times, you can image the performance for large-ish images.