Skip to content

Instantly share code, notes, and snippets.

@leonnnn
Forked from astro/autocrop.rb
Created November 22, 2008 23:35
Show Gist options
  • Save leonnnn/27981 to your computer and use it in GitHub Desktop.
Save leonnnn/27981 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
start_time = Time.now
require 'RMagick'
include Magick
infile, outfile = ARGV
unless infile && outfile
puts "Usage: #{$0} <infile> <outfile>"
exit 1
end
class ImageData
BLOCK_WIDTH = 10
BLOCK_HEIGHT = 10
def self.from_file(filename)
new Image.read(filename).first
end
def write_file(filename)
@img.write(filename)
end
def initialize(img)
@img = img
clamp!
end
def clamp!
crop!(0, 0, width, height)
end
def crop!(bx, by, bw, bh)
# puts "crop #{bx}x#{by}+#{bw}x#{bh} (#{@img.columns}x#{@img.rows})"
x, y = bx * BLOCK_WIDTH, by * BLOCK_HEIGHT
w, h = bw * BLOCK_WIDTH, bh * BLOCK_HEIGHT
@img = @img.crop(x, y, w, h, true)
# puts "=> #{@img.columns}x#{@img.rows}"
self
end
def entropy_of(bx, by)
# Raw pixels:
pixels = @img.get_pixels(bx * BLOCK_WIDTH, by * BLOCK_HEIGHT,
BLOCK_WIDTH, BLOCK_HEIGHT)
# Intensities as [ 0.0, 0.1, 0.2 .. 1.0 ]
intensities = pixels.map { |pixel| (pixel.intensity / 6553.5).to_i / 10.0 }
amounts = Hash.new(0)
intensities.each do |i|
amounts[i] += 1
end
probabilities = {}
amounts.each do |intensity,amounts|
probabilities[intensity] = amounts / (BLOCK_WIDTH * BLOCK_HEIGHT).to_f
end
- intensities.inject(0) do |sum,intensity|
sum + probabilities[intensity] * Math.log(probabilities[intensity])
end
end
def width
@img.columns / BLOCK_WIDTH
end
def height
@img.rows / BLOCK_HEIGHT
end
def block_void?(x, y)
entropy_of(x, y) < 1.0
end
AREA_VOID_THRESHOLD = 0.95
def area_void?(x, y, w, h)
voids = 0
(y..(y+h-1)).each do |by|
(x..(x+w-1)).each do |bx|
voids += 1 if block_void?(bx, by)
end
end
# puts "#{x}x#{y}+#{w}x#{h} => #{voids / (w * h).to_f}"
voids / (w * h).to_f > AREA_VOID_THRESHOLD
end
def print_out
height.times { |y|
width.times { |x|
if block_void?(x, y)
print " "
else
print "x"
end
}
puts
}
end
def intensity_of(bx, by)
pixels = @img.get_pixels(bx * BLOCK_WIDTH, by * BLOCK_HEIGHT,
BLOCK_WIDTH, BLOCK_HEIGHT)
sum = 0
pixels.each do |pixel|
# puts pixel
sum += (pixel.intensity / 6553.5).to_i / 10.0
end
# puts "sum = " + sum.to_s
sum / (BLOCK_WIDTH * BLOCK_HEIGHT)
end
BLACK_THRESHOLD = 0.1
def block_black?(bx, by)
intensity_of(bx, by) < BLACK_THRESHOLD
end
def print_black_out
height.times { |y|
width.times { |x|
if block_black?(x, y)
print "x"
else
print " "
end
}
puts
}
end
def area_black?(x, y, w, h)
blacks = 0
(y..(y+h-1)).each do |by|
(x..(x+w-1)).each do |bx|
blacks += 1 if block_black?(bx, by)
end
end
# puts "#{x}x#{y}+#{w}x#{h} => #{blacks / (w * h).to_f}"
blacks / (w * h).to_f > 0.99
end
def dimensions
@img.columns.to_s + "x" + @img.rows.to_s
end
end
d = ImageData.from_file(infile)
begin
done = true
# From top:
unless d.area_void?(0, 0, d.width, 1)
d.crop!(0, 1, d.width, d.height - 1)
done = false
end
# From left:
unless d.area_void?(0, 0, 1, d.height)
d.crop!(1, 0, d.width - 1, d.height)
done = false
end
# From bottom:
unless d.area_void?(0, d.height - 1, d.width, 1)
d.crop!(0, 0, d.width, d.height - 1)
done = false
end
# From right:
unless d.area_void?(d.width - 1, 0, 1, d.height)
d.crop!(0, 0, d.width - 1, d.height)
done = false
end
end while not done
begin
done = true
if d.area_black?(0, d.height - 1, d.width, 1)
d.crop!(0, 0, d.width, d.height - 1)
done = false
end
end while not done
d.write_file(outfile)
elapsed_time = Time.now - start_time
puts "Cropped " + infile + " in " + elapsed_time.to_s + "s. " + d.dimensions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment