It's called quantization, and it's complicated. I've worked extensively with this problem, and my best results have been using Octree quantization and a custom diffusion algorithm.
Your fastest point from A to B is grab my code (open-source, but $69 to download) and use the extremely simple API to set the color count to 16 and save as GIF or PNG. Should be about 2 lines of code if you want to do it via code-behind... or, you can use a querystring if it's on the filesystem:
image.bmp?format=gif&colors=16
If the image isn't already grayscale, you can do that using the ImageAttributes class of the module. The resulting GIF will automatically have a grayscale palette. Minimal work, great results.
Remember you don't have to use it as an HttpModule - it's primarily a library for resizing, modifying, and encoding images.
If you want to roll your own, here's what I started with:
http://codebetter.com/blogs/brendan.tompkins/archive/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness.aspx
Read through the comments and patch the pointer arithmetic errors per my comments....
No dithering, though, and you may have trouble running the original in less than a full trust environment. I've made a lot of patches over the years, and I don't remember them all.