HOWTO: Watermarking Images with ImageMagick and attachment_fu

January 14th, 2010

While working on a project for the State Hermitage Museum last year, I had to implement some image watermarking. The basic requirement was that for a certain type of uploaded image, its largest thumbnail should have the museum’s logo tiled across it. I was using attachment_fu to handle the image upload, and ImageMagick/RMagick to process the thumbnails.

After some cursory Googling, I found the ImageMagick Annotating guide, which had this sample watermark command:

 $ convert overlay.png  -fill grey50 -colorize 40  miff:- |\
    composite -dissolve 15 -tile  -  original.jpg watermarked_image.jpg

The dissection of the command:

overlay.png: The source image to overlay

-fill grey50 -colorize 40: Alter the colors of the watermark file

composite: command to overlay the watermark

-dissolve 15 -tile: “dissolve” the overlay at 15%, for good transparency, and tile (repeat) the watermark over the source image.

That’s simple enough, and with these source files:

overlay.png

and dearest Rufus:

rufus.jpg

rufus.jpg

 $ convert overlay.png -fill grey50 -colorize 40 miff:- |\
    composite -dissolve 15 -tile - rufus.jpg result-15.jpg

Produces:

Overlay with 15% dissolve

convert overlay.png -fill grey50 -colorize 40 miff:- |\
  composite -dissolve 50 -tile - rufus.jpg result-50.jpg

Overlay with 50% dissolve

Unfortunately, RMagick’s watermark method doesn’t support tiling. To work around, I had to call the composite_tiled! method on a colorized image. This code is in my Thumbnail model, which includes attachment_fu:

class Thumbnail < ActiveRecord::Base
  has_attachment  :content_type => :image,
   # some settings omitted
   :watermark_overlay => File.join(RAILS_ROOT, '/public/images/watermark-overlay-image.png'),
   :watermarkable_size => "1500>" 
 
  after_attachment_saved do |record|
    if record.respond_to?(:parent_id) and record.parent_id.nil? # the original image, not the smaller thumbnails
      with_image record.full_filename do |img|
        img.composite_tiled!(
          Magick::ImageList.new(attachment_options[:watermark_overlay]).first.colorize(0.4, 0.4, 0.4, 'grey'),   # process and colorize image
          Magick::SoftLightCompositeOp)
        img.write record.full_filename  # save image
      end
    end
  end
end

Now every Thumbnail record will automatically have a watermarked large image.

Gordie, Pretty Bird

January 8th, 2010

This is Gordie, the Gansen family pet bird. He’s been around since 1997, making him 12 years old. Words cannot express how much I love this little guy.

He loves nothing more than to run around and chase pieces of paper. If you tap on the paper, the following will happen:

And sometimes he gets a bit overstimulated and decides to bite everyone, then it’s time to hang out in the cage:

Oh, Gordie. happy new year to you.

you should concentrate on finalizing this transaction rather than responding to negative thoughts

December 22nd, 2009

This junk email wiggled its way through Google’s filtering, much to my delight. After reading it, I felt genuinely touched, as if Deborah was really worried for me, worried that I wouldn’t claim my money.

Oh, Deborah, if I ever make it over to Benin to collect my fifteen thousand United States dollars, I’ll give you a hug.

Email in full:

Fund Beneficiary,
Welcome to ECO BANK Money Gram money transfer Cotonou Benin Plc,
Beneficiary! This is to notify you that we have concluded your payment through Money Gram.

Here is your 3payment MTCN of USD$15,000.00 united state dollars $5,000.00 each, First payment, MTCN 212-314-53 (2) 805-217-78 (3) 968-584-30 and we will give you the sender name to pick up this money immediately you send the activation remittance permit fee which is $188.00 dollars to the name below.

Receiver Name == Augustine Okolo
Address: …. Benin / City Continuo
Test Q…………What?
Ans ……………..Payment.
Amount $188.00

Kindly send the activated remittance permit fee with the given infor…and call for sender name on +22 998 377 738 Urgent,
The grace given to you, is a previledge, not a right, therefore it must not be abused. When i imagine the magnitude of your compensation funds left in our possession,it pains me to see that you are yet to get your transfered.

You should count yourself extremely luck for the fortune that has come your way today . It is one in a life time fortune and you should concentrate on finalizing this transaction rather than responding to negative thoughts.

As a friend, i will not like you to loose this chance which has come up today because not every body in this life has this great chance that has come your way including me.Act fast in remitting the fee and let us finalize this transaction which has already stayed longer than expected. I await your swift response, comply and details

Sir. M. Deborah Utecht
FORIGN OPERATION MANAGER
MONEY GRAM OFFICE BENIN REPUBLIC )

I have purchased but a single song from iTunes.

October 21st, 2009

iTunes

But what a song it is.

HOWTO: Remove Byte-order Mark with Ruby and Iconv

October 19th, 2009

I’m working on a small project that involves loading a UTF-16LE (16-bit Unicode, Little Endian) CSV file, converting it to UTF-8 (normal Unicode, as it may be) with iconv, then parsing the values with FasterCSV. Everything was working fine except for loading the first column of data by the column header value. For example, given data:

First Name Last Name Email
Jimbo Jones jimbo.jones@example.com

I could access column 2 (Last Name) as either row.field("Last Name") or row.field(1). However, if I tried to access the first column using row.field("First Name"), it would return nil. row.field(0), on the other hand, would return the proper value.

Hmmmm.

After some sleuthing, I examined the raw content of the string:

(rdb:1) p row.headers.first.unpack('C*')
[239, 187, 191, 70, 105, 114, 115, 116, 32, 78, 97, 109, 101]

Ah, ha! The first three characters are the byte-order mark, or BOM. Ruby, for whatever reason, does not strip it when reading a file as input, so it’s passed along in the input stream. When loading a file with FasterCSV, it’ll keep those characters in the key name, causing lookups by the first column key name to return nil.

I modified my file conversion code as follows:

  def convert_to_utf8
    # Data files are exported as Little Endian UTF-16. We need to parse as UTF-8
    contents = File.open(@file_name).read      
    begin
      converted = Iconv.iconv('UTF-8', 'UTF-16LE', contents)
      converted.first.gsub!("\xEF\xBB\xBF", '') # strip the BOM (byte order mark) from the first line of input
      output = File.open(@file_name, 'w')
      output.write(converted)
    rescue Iconv::Failure
      puts $!.inspect
    end
  end

And all is well in the world.

An incomplete list

September 11th, 2009

A list of names I have, at one point or another, used to refer to my girlfriend’s dog:

  • Ruf
  • Rufito
  • Rufee
  • Stinky
  • Poops
  • Poops McGee
  • Poop Machine
  • Lil’ Pooper
  • Shit for brains (learned that one at the Sam Mazzara School of Driving, another story, for another time)
  • Stink Machine
  • (the) Nugg
  • Nugget
  • El Nuggo
  • Big Dummy
  • The BEAST (ALL CAPS)
  • (Wee) Lil’ Beastie
  • The Mayor of Cullerton

And for the record, his name is Rufus.

Superior strength

August 20th, 2009

rsync

August 13th, 2009

Note that doubling a single-quote inside a single-quoted string gives you a single-quote; likewise for double-quotes (though you need to pay attention to which quotes your shell is parsing and which quotes rsync is parsing).

rsync man page

Ow, my head hurts.

Directory tree

August 10th, 2009

A handy Bash script to display a tree view of a directory, adapted from http://www.centerkey.com/tree. This version omits .svn and .git directories, and uses the find utility.

echo
if [ "$1" != "" ]  #if parameter exists, use as base folder
   then cd "$1"
   fi
pwd
find . \! \( -path "*.svn*" -or -path "*.git*" \) -type d | \
   sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/   /' -e 's/-/|/'
if [ `ls -F -1 | grep "/" | wc -l` = 0 ]
   then echo "   -&gt; no sub-directories"
   fi
echo
exit

Example use:

[cgansen@Crystal-Frontier ~]$ tree projects/self.d-struct.org/wp-admin/
 
/Users/cgansen/projects/self.d-struct.org/wp-admin
   .
   |-css
   |-images
   |-import
   |-includes
   |-js
 
[cgansen@Crystal-Frontier ~]$

Assembling Coil

July 6th, 2009

A few weeks ago, John and I dropped by to see our friend Craig exhibiting at the Guerrilla Truck Show in Chicago’s West Loop area. In spite of the pouring rain, we had a great time seeing the exhibits, and I walked away the proud owner of one of Craig’s recent designs, the Coil Lamp. After a few weeks of ignoring it, as I was busy moving into a (totally awesome) new apartment in Pilsen, I finally found a few free minutes to assemble it.

Raw materials

Starting off, I gather the necessary items: lamp with signed dedication, 100′ extension cord, CFL bulb OMG DO NOT USE A INCANDESCENT BULB (the instructions clarified that no fewer than three times), and a beer. My suggested pairing for assembling Coil: Goose Island Summertime. The crisp finish accentuates the sharp, precise laser cut of the clear acrylic form of Coil, and they’re both made here in Chicago.

Instruction manual

The instructions are detailed and nicely illustrated.

The raw form of Coil

Unravelled cord

Unravelled cord

My only mistake was not taking enough time to straighten out the coiled cord, which was pretty gnarled up, which caused me to have a bit of trouble getting a really smooth, even “coil” on the form.

Beginning to take form

First step

First few spins

First few spins

Flipped per instructions

Flipped per instructions

Notice as the beer is slowly drained, the sign of real progress on any project.

Ta-da! Completed Coil

In situ

There you have it, an assembled Coil Lamp. At this moment, it is sitting in the window, casting a cool, fluorescent glow on the streets of Pilsen. More photos of the experience are online over at Flickr.