Created
April 24, 2012 03:26
-
-
Save EmmanuelOga/2476153 to your computer and use it in GitHub Desktop.
RMagick autocropper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'RMagick' | |
# Auto crop an image by detecting solid edges around them. | |
class AutoCropper | |
# How small we accept a cropped picture to be. E.G. if it was 100x100 and | |
# ratio 0.1, min output should be 10x10 | |
MIN_CROP_RATIO = 0.1 | |
attr_reader :img, :x0, :y0, :x1, :y1, :min_width, :min_height, :rows, :columns, :is_border | |
def initialize(img, is_border_proc = nil, min_ratio = MIN_CROP_RATIO) | |
@img, @min_ratio = img, min_ratio | |
# Coordinates | |
@x0, @y0 = 0, 0; @x1, @y1 = img.columns, img.rows | |
@min_width, @min_height = img.columns * @min_ratio, img.rows * @min_ratio | |
# We need a border finder proc. Provide one if none was given. | |
@is_border = is_border_proc || self.class.default_border_finder(img) | |
find_edges | |
end | |
def width | |
x1 - x0 | |
end | |
def height | |
y1 - y0 | |
end | |
def output | |
img.crop x0, y0, width, height, true | |
end | |
# Returns a Proc that, given a set of pixels (an edge of the image) decides | |
# whether that edge is a border or not. | |
def self.default_border_finder(img, samples = 5, threshold = 0.95, fuzz = 0.05) | |
fuzz = (2**16 * fuzz).to_i | |
# Returns true if the edge is a border. | |
lambda do |edge| | |
border, non_border = 0.0, 0.0 | |
pixels = (0...samples).map { |n| edge[n * edge.length / samples] } | |
pixels.combination(2).each { |a, b| a.fcmp(b, fuzz) ? border += 1 : non_border += 1 } | |
border.to_f / (border + non_border) > threshold | |
end | |
end | |
private | |
def find_edges | |
return unless is_border | |
u = x1 - 1 | |
x0.upto(u) { |x| width_croppable? && is_border[vline x] ? @x0 = x + 1 : break } | |
(u).downto(x0) { |x| width_croppable? && is_border[vline x] ? @x1 = x - 1 : break } | |
u = y1 - 1 | |
0.upto(u) { |y| height_croppable? && is_border[hline y] ? @y0 = y + 1 : break } | |
(u).downto(y0) { |y| height_croppable? && is_border[hline y] ? @y1 = y - 1 : break } | |
end | |
def vline(x) | |
img.get_pixels x, y0, 1, height | |
end | |
def hline(y) | |
img.get_pixels x0, y, width, 1 | |
end | |
def width_croppable? | |
width > min_width | |
end | |
def height_croppable? | |
height > min_height | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment