class Mork::Magicko

@private Magicko: low-level image management, done in two ways: 1) direct system calls to imagemagick tools; 2) via the MiniMagick gem

Attributes

height[R]
width[R]

Public Class Methods

new(path) click to toggle source
# File lib/mork/magicko.rb, line 12
def initialize(path)
  @path = path
  @cmd = []
  # a density is required for processing PDF or other vector-based images;
  # a default of 150 dpi seems sensible. It should not affect bitmaps.
  density = 150
  # inspect the source file
  s1, s2, s3 = Open3.capture3 "identify -density #{density} -format '%w %h %m' #{path}"
  if s3.success?
    # parse the identify command output
    w, h, @type = s1.split(' ')
    @width  = w.to_i
    @height = h.to_i
    if @type.downcase == 'pdf'
      # remember density for later use
      @density = density
    end
  else
    # Inspect the stderr and raise appropriate errors
    case s2
    when /No such file/
      fail Errno::ENOENT
    when /The file has been damaged/
      fail IOError, 'Invalid image. File may have been damaged'
    else
      fail IOError, 'Unknown problem with image file'
    end
  end
end

Public Instance Methods

check(coords, rounded) click to toggle source
# File lib/mork/magicko.rb, line 80
def check(coords, rounded)
  @cmd << [:stroke, 'red']
  @cmd << [:strokewidth, '3']
  coords.each do |c|
    @cmd << [:draw, "line #{c.cross1}"]
    @cmd << [:draw, "line #{c.cross2}"]
  end
end
highlight(coords, rounded) click to toggle source

Constructing MiniMagick commands

# File lib/mork/magicko.rb, line 67
def highlight(coords, rounded)
  @cmd << [:stroke, 'none']
  @cmd << [:fill, 'rgba(255, 255, 0, 0.3)']
  coords.each { |c| @cmd << [:draw, shape(c, rounded)] }
end
highlight_area(c) click to toggle source
# File lib/mork/magicko.rb, line 98
def highlight_area(c)
  @cmd << [:fill, 'none']
  @cmd << [:stroke, 'yellow']
  @cmd << [:strokewidth, 3]
  @cmd << [:draw, "rectangle #{c.rect_points}"]
end
highlight_rect(areas) click to toggle source
# File lib/mork/magicko.rb, line 105
def highlight_rect(areas)
  return if areas.empty?
  @cmd << [:fill, 'none']
  @cmd << [:stroke, 'yellow']
  @cmd << [:strokewidth, 3]
  areas.each do |c|
    @cmd << [:draw, "rectangle #{c.rect_points}"]
  end
end
join(p) click to toggle source
# File lib/mork/magicko.rb, line 115
def join(p)
  @cmd << [:fill, 'none']
  @cmd << [:stroke, 'green']
  @cmd << [:strokewidth, 3]
  pts = [
    p[0][:x], p[0][:y],
    p[1][:x], p[1][:y],
    p[2][:x], p[2][:y],
    p[3][:x], p[3][:y]
  ].join ' '
  @cmd << [:draw, "polygon #{pts}"]
end
outline(coords, rounded) click to toggle source
# File lib/mork/magicko.rb, line 73
def outline(coords, rounded)
  @cmd << [:stroke, 'green']
  @cmd << [:strokewidth, '3']
  @cmd << [:fill, 'none']
  coords.each { |c| @cmd << [:draw, shape(c, rounded)] }
end
plus(x, y, l) click to toggle source
# File lib/mork/magicko.rb, line 89
def plus(x, y, l)
  @cmd << [:stroke, 'red']
  @cmd << [:strokewidth, 1]
  pts = [ x-l, y, x+l, y ].join ' '
  @cmd << [:draw, "line #{pts}"]
  pts = [ x, y-l, x, y+l ].join ' '
  @cmd << [:draw, "line #{pts}"]
end
registered_bytes(pp) click to toggle source

registered_bytes returns an array of the same size as the original image, but with pixels stretched out based on the passed perspective points (i.e. the centers of the four registration marks) pp: a hash in the form of pp[:x], pp[:y], etc.

# File lib/mork/magicko.rb, line 50
def registered_bytes(pp)
  read_bytes "-distort Perspective '#{pps pp}'"
end
rm_patch(c, blr=0, dlt=0) click to toggle source

Reading from the image file the bytes from one of the four corner squares encompassing each registration mark; the blur and dilation manipulations may prevent registration misalignments due to stray dark pixels

# File lib/mork/magicko.rb, line 57
def rm_patch(c, blr=0, dlt=0)
  b = blr==0 ? '' : " -blur #{blr*3}x#{blr}"
  d = dlt==0 ? '' : " -morphology Dilate Octagon:#{dlt}"
  read_bytes "-crop #{c.cropper}#{b}#{d}"
end
save(fname, reg) click to toggle source
# File lib/mork/magicko.rb, line 128
def save(fname, reg)
  MiniMagick::Tool::Convert.new(whiny: false) do |img|
    img << '-density' << @density if @density
    img << @path
    img.distort(:perspective, pps(reg)) if reg
    @cmd.each { |cmd| img.send(*cmd) }
    img << fname
  end
end
valid?() click to toggle source
# File lib/mork/magicko.rb, line 42
def valid?
  @valid
end

Private Instance Methods

pps(pp) click to toggle source

perspective points: brings the found registration area centers to the original image boundaries; the result is that the registered image is somewhat stretched, which should be okay

# File lib/mork/magicko.rb, line 155
def pps(pp)
  [
    pp[:tl][:x], pp[:tl][:y],     0,      0,
    pp[:tr][:x], pp[:tr][:y], width,      0,
    pp[:br][:x], pp[:br][:y], width, height,
    pp[:bl][:x], pp[:bl][:y],     0, height
  ].join ' '
end
read_bytes(params=nil) click to toggle source

calling imagemagick and capturing the converted image into an array of bytes

# File lib/mork/magicko.rb, line 146
def read_bytes(params=nil)
  d = @density ? "-density #{@density}" : nil
  s = "|convert -depth 8 #{d} #{@path} #{params} gray:-"
  IO.read(s).unpack 'C*'
end
shape(c, rounded) click to toggle source
# File lib/mork/magicko.rb, line 140
def shape(c, rounded)
  rounded ? "roundrectangle #{c.choice_cell}" : "rectangle #{c.rect_points}"
end