My solution relies on having RMagick do all the work. Here's the output 
for the test image:

+-------------------------------------------------------------------------+
|                      .::+====+::                                        |
|                  .:==o======ooo*oo+.                                    |
|                 +oo=====++++===oo***+                                   |
|               :oo==++++===++==ooooo*^*.                                 |
|              :oo====++++====oooooooo*^*                                 |
|             .oo=====++++========ooo**^^o                                |
|            .ooo=====++++++======ooo**^^%:                               |
|           :*oo*o==============oooo****^^^.                              |
|          :ooo$WW^===========oooo****^^^^^=                              |
|          ooo*WWW%=========oooo^^*^**^^^^^^                              |
|          oooo=o::o=====oooo*%WWW$^^*^^^^*^:                             |
|          =ooo=:+====ooooo**o^WWW$***^****^:                             |
|          .*o*o*ooooo==ooo*^*+====o*^*****^.                             |
|           :o%$$$$$WW$$$%%^^^^**********^^=                              |
|            .oWWWWWWWWWWWWWW$%%^**o****^^=                   ::::::      |
|              %$$$$$$$W$$W$WWWW$^****^^o.              ...::::::::==:    |
|              =WWWW$$$W$$$$WW$$%^^****+. .........:::::::::::::::::=o+   |
|              :$WWWWWWWWWWWW%^****^%%%%%^^**oo====++:::::::::::::::+=o+  |
|           :+oo*$$WWWWWWW$%^*^^^^^%$$$%%%^**o===+:::::::::::::::::++==o: |
|         .:====o*^%$$$$$%^^%%^^^^%%%%%%%^^**o===+::::::::::::::::::===oo |
|       .:+++++==o*^%%%%%%$%%%%^^^^%%%^^^**oo===++:::::::::::::::::+==ooo |
|     .:+::::+++=oo*^%%%$%%%%%^^^^^*****ooo===+++::::::::::::::::++===oo= |
|    :++::::::+++=oo*^%%%%%%^^****ooooo=====++:::::::::::::++:::++====o*. |
|   :+::::::::::+===o**^^^^**ooo=====+=+++++::::::::::::+::==++++===ooo+  |
|  :+::::::::::::+++==oooooo====+::::+:::::+::::+::++::+::+==++=====oo=   |
| .+::::::::::::::::++=+=====+::::::+:::::+::::+:::+:::+++========ooo=    |
| :+::::::::::::::::::+:++++::::::++::::::::::+:::++:++==========ooo+     |
|.++++:::::::::::::::::::::::::::+:::::+::::+:::++++===========oooo:      |
|.++++::::::::::::::::::::::::::::::++:+::+=+++==============oooo=.       |
| +++++++::::::::::::::::::::::++::++::+==+=+===========o===ooo=:         |
| :==++++::::+:::::::::+:+:::::::::+++===++=============ooooo=:           |
| .===++++++++++::+++::+++:+++++::+===++====o==========o=o==:.            |
|  :=======+++++++++++++++++:++===============oo====oo=o==:.              |
|   :====================++++++==+=========o=====ooo====:                 |
|    .==================================ooooooo=oo===+:                   |
|      :+=============================oooo=o==oo==::.                     |
|        ::==========================o====oo===::.                        |
|           ::++==============+===========+::.                            |
|              ..:::++++==++=+++++++++::..                                |
|                    ....:.::::::...                                      |
+-------------------------------------------------------------------------+


require 'RMagick'

CHARS = ['W', 'M', '$', '@', '#', '%', '^', 'x', '*', 'o', '=', '+', 
':', '~', '.', ' ']
FONT_ROWS = 8
FONT_COLS = 4

img = Magick::Image.read(ARGV[0] || "Flower_Hat.jpg").first

# Resize too-large images. The resulting image is going to be
# about twice the size of the input, so if the original image is too
# large we need to make it smaller so the ASCII version won't be too
# big. The `change_geometry' method computes new dimensions for an
# image based on the geometry argument. The '320x320>' argument says
# "If the image is too big to fit in a 320x320 square, compute the
# dimensions of an image that will fit, but retain the original aspect
# ratio. If the image is already smaller than 320x320, keep the same
# dimensions."
img.change_geometry('320x320>') do |cols, rows|
	img.resize!(cols, rows) if cols != img.columns || rows != img.rows
end

# Compute the image size in ASCII "pixels" and resize the image to have
# those dimensions. The resulting image does not have the same aspect
# ratio as the original, but since our "pixels" are twice as tall as
# they are wide we'll get our proportions back (roughly) when we render.
pr = img.rows / FONT_ROWS
pc = img.columns / FONT_COLS
img.resize!(pc, pr)

img = img.quantize(16, Magick::GRAYColorspace)
img = img.normalize

# Draw the image surrounded by a border. The `view' method is slow but
# it makes it easy to address individual pixels. In grayscale images,
# all three RGB channels have the same value so the red channel is as
# good as any for choosing which character to represent the intensity of
# this particular pixel.
border = '+' + ('-' * pc) + '+'
puts border
img.view(0, 0, pc, pr) do |view|
     pr.times do |i|
         putc '|'
         pc.times { |j| putc CHARS[view[i][j].red/16] }
         puts '|'
     end
end
puts border