home
on exploration, introspection and creation

Archive for the ‘code’ Category

My own alarm

Thursday, January 7th, 2010

I wanted to wake up to NPR. There’s a good alarm application for the Mac called Alarm Clock which allowed me to play an arbitrary iTunes playlist on schedule (with bells and whistles such as gradually increasing the volume), but the free version couldn’t deal with playing audio streams (such as, in my case, wnyc.org).

No problem — I used cron as well as OS X’s built-in wake-on-schedule functionality.

First, in System Preferences > Energy Saver, I set a schedule to wake up the computer on weekdays at 6am. Then I edited the crontab: in Terminal I typed crontab -e and typed in the editor

1 6 * * 1-5 osascript /Users/strozek/wnyc.applescript

The above tells OS X to run the command osascript at 6:01am Monday through Friday. The script I pass to osascript is the following script:

set volume 2
tell application “Safari”
activate
open location “http://www.wnyc.org/flashplayer/player.html#/playStream/fm939″
end tell
delay 20
set volume 2
delay 20
set volume 2.75
delay 20
set volume 3.25
delay 20
set volume 3.75
delay 20
set volume 4.25
delay 20
set volume 5

And voilĂ ! I just need to remember to keep the computer plugged in at night and not close the lid.

Color Pickers

Sunday, November 29th, 2009

If you’ve looked at the crowdsourced art experiment you may have noticed a little color picker on the page. It looks like this:

My Color Picker

My Color Picker

If you’ve seen other color pickers (for example in Photoshop, the Office / iWork packet, or even MS Paint) you’ll notice that this one is slightly different. I made it to have property that a lot of other pickers lack: it displays all possible colors on one two-dimensional plane. If you think about it, the other pickers you’ve seen either have an additional slider that changes the 2D plane (e.g. Photoshop or the built-in Windows or OS X one) or they don’t allow you to specify some colors (e.g. the Pantone picker or a lot of the pickers online).

As you know, I’m fascinated with color, especially when there’s math or technology involved. My picker happens to involve both and so here we go.

The color space is three-dimensional, that is, to uniquely identify a color, you have to specify three dimensions. There are many ways to specify a color: the most common one (albeit not the more natural one to understand) is the RGB space: each color is identified by the intensity of pure red, pure green and pure blue components that, when mixed together additively, construct the desired color. I emphasize additively because when we think about “mixing” color (when we paint with oil or watercolor) we don’t actually compose color the same way our eye does. In the RGB space, there are three numbers you specify for each color: and so yellow can be defined as (1.0, 1.0, 0.0) because it consists of max intensity red and green and no blue. Violet (like the color of this flower) can be defined as (0.6, 0.3, 0.7).

Similarly, there are other ways to describe a color: the HSL space, that I find fairly intuitive, describes a color by specifying its hue (the location on the rainbow), its saturation (how vibrant is the color) and lightness (how bright it is). Again, three dimensions (why the color space is three-dimensional is an interesting question…).

This fact makes it hard to design user interface elements that allow you to pick a color (color pickers): the screen is two-dimensional so you either need a slider or some other way to change the third dimension, or you will see a selection of all colors. Being a visual person, I wanted to have a picker that displays all the colors at once, without some stupid slider.

My first attempt took advantage of the fact that on a computer, every measure is discrete (there is no such thing as infinity in computing) so I can collapse the three dimensions into two simply by cleverly rearranging where each colors should be placed. Assuming that each color component has 256 degrees of intensity (which is the case in computer screens these days), we can list all colors, for example

(0, 0, 0), (0, 0, 1), …, (0, 0, 255), (0, 1, 0), (0, 1, 1), …, (255, 255, 255)

We can now map the sequence into a two-dimensional one, for example

(0, 0), (0, 1), …, (0, 4095), (1, 0), (1, 1), …, (4095, 4095)

The problem, however, is that we want the mapping to be smooth, i.e. ideally we would like nearby pixels to have similar colors, and the mapping above (and in fact most mappings) won’t guarantee this.

This is where math comes in handy, specifically the field of space-filling curves. I kept one dimension (say, the blue component)–that will be my X coordinate of the resulting picker. For the other, I used a Hilbert-like curve to collapse the green and red components into one Y coordinate. A nice thing about Hilbert curves is that they are somewhat smooth: in this picture, if you pick two neighboring points they are likely to be close to one another on the curve. I’ll leave as an exercise for the reader to determine the actual expected Euclidean distance in color intensity for randomly picked two neighboring points on a Hilbert curve.

Well, such was the first experiment. The resulting “color belt” was very long (because collapsing two dimensions into one makes it a very long dimension!) and the lack of smoothness was pretty obvious (click to view the full image):

The color belt

My first approach to create a two-dimensional complete color picker

I then took the belt and made it into a ring, by “curving” the long dimension around (to take advantage of the fact that the circumference gives me 2π more space than the radius:

The color ring

The color ring

Besides looking very Tolkenian, the ring has a pleasing æsthetic to it. Still, it’s somewhat hard to pick out the color you want because the colors are fairly scattered (if your mouse if off by one pixel you may be picking a totally different color).

The next approach was to be a little smarter with the choice of dimensions. Instead of picking one of the components to be one dimension and try to collapse the other two, I chose the light intensity of a color as the horizontal dimension and then collapsed all colors of a given intensity into the vertical dimension. The advantage of this approach was an increased smoothness: all colors along a vertical line had the same intensity; and because the intensity function is linear in the values of R, G and B (it’s a weighted average of the red, green and blue intensity), the colors on a horizontal line are all similar.

I also varied the order in which colors would be placed on a vertical line, based on a heuristic of the R, G and B component. Some of the choices gave very interesting results. For example, if I plotted each vertical line based on the lexicographical order (sort by R, then G, then B), I got a picker that looked like this:

Intensity-based picker; pixels ordered lexicographically (R, G, B)

Intensity-based picker; pixels ordered lexicographically (R, G, B)

I got really interesting results when I made the heuristic slightly more complex: for example, if I ordered the pixels by the value of the sum of squares of the colors, I got

Intensity-based picker; pixels ordered by R^2+G^2+B^2

Intensity-based picker; pixels ordered by R^2+G^2+B^2

Finally, in order to bundle similar colors together (i.e. have fewer “boundaries” where neighboring colors differ significantly at the expense of more pronounced boundaries), I used a heuristic that “favored” one component over another. The image on the right, for example, orders pixels like this:

  • In first order, pixels whose blue component is higher than their red component, appear higher in the image
  • Then pixels whose green component is higher than their blue component
  • Finally pixels whose red component is higher than their green component
Heuristic 1 Heuristic 2

Intensity-based picker; pixels ordered based on prevalent component (two different heuristics)

The little picker at the very top of this post is a slightly massaged (reduced and despeckled, to make picking more accurate) version of this approach with a lexicographical order of pixels. I also added a pure gray gradient on the side–even though the picker itself contains every shade of gray, they are scattered throughout the image. If you’re interested, here is what the picker looks like if I order the pixels by saturation, it looks… phantasmagorical:

Intensity-based picker; pixels ordered by saturation

Intensity-based picker; pixels ordered by saturation

Source code for all color picker generators:

  • Color belt and color ring generator: ring.php (it generates a PNG file, so redirect the stdout to a file, say output.png)
  • A useful cheatsheet of color conversions (source unknown): colorConversions.txt
  • Layered color picker: picker.c (if generate a PBM file that Photoshop/ImageMagick can read, so redirect the stdout to a file, say output.pnm)

The zoom effect

Monday, October 19th, 2009

As I considered the best way to display series of pictures on the main page, I came up with an interesting way to compress a lot of information onto fairly limited screen real estate. The idea was to have a kind of a slide show composed of small icons that turn larger as you hover over them; clicking on any icon would bring up with full-size image. That way I could fit a lot of small (32×32 pixels) icons of images on the screen, yet offer the users the ability to browse larger versions (67×67 pixels) easily just by moving the mouse around. The idea, of course, was inspired by what OS X does with the Dock (an effect which, sadly, I have disabled on my computer–but due to different use scenarios). Check out the effect in action here.

The design process I went through is an interesting example of discovery (or serendipity, rather) and how taking an analytical approach doesn’t always yield the best results.

The desired effect will be very familiar to you if you’ve used OS X and the Dock. I want to display a series of small thumbnails of images in a row. If you hover over them, the image that your mouse is closest to gets larger, pushing out the other images if necessary. I wanted the effect to be smooth (so as you move your mouse over the row, images get bigger as they approach the mouse pointer, and then get smaller) and resemble something like this:


zoom-1zoom-2

The Zoom Effect

There are three variables that I need to be concerned about: how much to magnify the icons by (in my case, I wanted to go from 32 pixels to a maximum of 67 pixels), how far out the magnification should affect the icons (in the picture above, the icons two to the right of the center icon are no longer magnified), and how quickly the magnification should drop out (how “drastic” the magnification of the center icon should appear). For each of the icons in the row, I need to figure out how much to magnify them (by convention, let’s say that 1 is full magnification and α is the regular, small size) and where to place them horizontally (because they will push out other icons), subject to the constraint that the icons must remained aligned in a row.

An analytical solution was easy to get to, but very quickly spiraled out of control, and here is how. Let’s consider two configurations:

  • When the mouse cursor is exactly in the center of an icon, by symmetry that icon should have the maximum magnification:
    Maximum magnification at center of icon

    Maximum magnification at center of icon

  • When the mouse cursor is exactly in between two icons, also by symmetry both icons should be of equal size:
    Equal magnification in between two icons

    Equal magnification in between two icons

Depending on β, the magnification will drop out quickly (if β is close to α) or slowly (if it’s close to 1).

Since we want the magnification of the icon to be a smooth curve (as the mouse pointer moves across the icons), we simply need to define a continuous function given the three points it goes through: (0, 1) (because at x=0 — i.e. when the mouse cursor is exactly over the icon’s center, we want the magnification to be maximum), (α/2, β) (because when we’re in between two icons — i.e. a distance α/2 away from the center of one — we want the magnification to be β) and (Z, α) (the distance at which all magnification ceases). An exponential curve is the simplest one that we can try:

Magnification as a function of distance from the icon's center

Magnification as a function of distance from the icon's center

We will then be able to use this curve to determine how much to magnify each icon by. The icons will be sized such that their size given the distance between their center and the mouse pointer can be read off of that magnification curve:

Applying the magnification curve to each icon.  Past the point <i>Z</i> all icons retain their original, small size

Applying the magnification curve to each icon. Past the point Z all icons retain their original, small size

First let’s figure out the full form of the magnification curve. The curve must go through the two endpoints we identified, and be exponentially decaying, so it is of the form

y = 1 – (x/Z)P(1-α)

(We can verify that at 0, y=1 and at Z, y=α). We need to compute P based on the third point:

β = 1 – (α/2Z)P(1-α)
P = logα/2Z( (1-β)/(1-α) )

The first icon is simple: determine the distance between the mouse pointer and the center of the icon and use the curve above to read off the magnification (it will be something between β and 1). The subsequent icons are a little more tricky, because in order to figure out the magnification you have to know how far its center is from the mouse pointer, but the position of the center is a function of magnification! At this point the easiest thing to do is to solve this numerically, by simply iterating over all possible positions of the center and determining the closest one (since we’re operating in a discrete space with the smallest effective resolution of 1 pixel).

While each step seems fairly straightforward, the end result is a pretty big hairball. Being lazy, I realized that there must be a better solution to this problem.

And then I realized that so long as the illusion of smoothness is preserved, some simplifying assumptions can be made. First of all, the exponential curve I used initially was too complicated and looked too discontinuous at large magnifications (because of a sharp spike near 0); there must have been something else that’s straightforward to compute. The parameters seemed complicated, too — α and Z could be replaced with just one — a measure of how quickly the magnification should decay — without much loss of the effect.

The Normal curve came to mind — with just one parameter (σ) it was much easier to experimentally determine a value that had a pleasing effect (plus, σ is by definition very close to our notion of “how quickly this should decay”). I also got rid of the self-referential problem (determining magnification requires knowing origin, but origin influences magnification) by looking at not the actual distance (how far is the icon from the mouse pointer after all icons have been magnified), but original distance (how far is the icon from the pointer before magnification).

The resulting algorithm is much more elegant — and produces a more visually pleasing effect:

  • For each icon in the original (i.e. before any magnification takes place) series, determine how far its center is from the mouse pointer (I experimented with just using the x-coordinate, but the nice thing about this algorithm is that any smooth function works, and the actual distance produced a nicer effect than just the horizontal distance)
  • Use the Normal curve to determine its magnification. We want the result to be 1 if the distance is 0 (i.e. the icon is directly under the mouse pointer) and α if the distance is infinite (since the Normal curve dies off quickly, the size would go down to α pretty quickly as well), i.e.

    N = e-d*d/2σ*σ
    M = N+α(1-N)

  • Place each icon with its magnified size on screen; keep track of how much space each icon took so that subsequent icons can be displayed after it and not on top of it
  • Technically this is enough for magnification. However, this doesn’t produce a smooth effect: since the icons are always pushed out to the right, the “tail” of icons keeps traveling back and forth. We want the entire series of move smoothly, slowly to the left as the mouse moves to the right (go here and watch the icons at the end of the series travel to the left as you move your mouse pointer left to right, across the icons). This is simple to correct, though: keep track of how much space all the icons take (by adding up each size as you go), and then offset all the icons by a fraction of that total space, depending on where the mouse pointer is: suppose the icons originally take d pixels, and expanded they all take D pixels, and the mouse pointer is at position x (between 0–at the beginning of the series–and d), we want to offset all icons by

    x*D/d

To check out the source code for this effect, see ui.js, a javascript file that includes a function shiftImages (I know, bad name) to magnify all icons based on the position of the mouse pointer.

You can also download all diagrams on this page in pdf form and in LaTeX.

Crowdsourcing Art, Part Two

Thursday, September 24th, 2009

Previously I introduced the idea of a crowdsourced graphic that I would have my friends generate. The experiment is still happening, and I encourage everyone who got a token to participate (so far very few people actually submitted their pixels). If you didn’t get a token, but are subscribing to/occasionally reading/just stumbled on this blog, let me know and I’ll send you one. Similarly, if you used up your tokens, let me know. I don’t want the lack of pixels (or the fear to lose them) to be the reason why people don’t contribute.

The work so far is fun to look at; undisputedly predictable was the penis that found its way on the canvas a few hours after the game started, but there have been efforts to turn it into a happy face (I must admit, I was tempted to use up some of my tokens but in the end I decided this piece of art is a no judgment art and anything goes).

An interesting pattern was that people used the background image and the fact that the canvas was translucent to trace elements of the image onto the canvas. I did not expect that, but I guess that’s an as good place to start as any.

Finally, people are not taking advantage of the collaboration element of the game. I’ll let you in on a secret: the “bonus” you get for merging tokens is actually pretty substantial. In fact, the number of pixels you can set is equal to the “size” of the token (the first three digits) raised to the power 1.25. This may not seem much (it’s close to 1, after all), but consider merging two tokens, each of “size” 10. Each individual token allows you to set 101.25 = 18 pixels, so in total both tokens let you set 36 pixels. If you merge the tokens, you will get one token of size 20 and 201.25 = 42 (you’ve just gained 6 pixels). The differences are even larger for larger tokens (or more tokens). Two tokens of size 20 give you 84 pixels when separate and 101 when merged! You get the idea.

Finally in the spirit of full collaboration I’m making the source code available — I’m not opening up the interface (which contains secret information that allows me to generate and keep track of tokens and make sure people don’t cheat) but most of the implementation is in the helper file below.

Source:
crowdsource-helper.rb

Crowdsourcing Art

Saturday, September 19th, 2009

Inspired by this guy I thought of something that might be fun: to have the community generate a small image collaboratively by having everyone be able to paint a few pixels of it. In a way, I wanted to crowdsource the generation of a computer graphic. This became my miniproject for the past weekend, and also a good way to announce the Grand Opening of this site (until now nobody really knew about it).

The idea is quite simple: consider a 67×67 pixel image. It’s pretty small, but sufficient to capture quite a lot of detail, e.g.

A lot of detail can fit in a 67x67 image

A lot of detail can fit in a 67x67 image

The image starts fully transparent. I then give away tokens to people I know; each token gives the owner the right to put a small number of pixels (say, 15) on the image. Everyone is free to pick what colors each of the pixels should be, and which pixels to set. If I give away enough tokens, and if people want to participate, interesting results can be generated (all without my involvement at all!)

I’m going to make this a little more fun by introducing two rules:

  • You can “draw” over other people’s pixels but that’s going to cost you more (2 pixels instead of 1) — this is to imitate what artists do, often painting over sections of their image multiple times
  • You are rewarded for collaborating with others. If two people get together, they can “merge” two tokens into one, and, just like a two-liter Cola costs a little less than two one-liter Colas, such combined token allows you to draw more pixels than what the original tokens allowed. (Similarly, splitting a token into two pieces is allowed, but the resulting two pixel “allowances” will add up to a little less than the original).

The second rule is, in my view, very interesting and I expect some fascinating results. Since one person needs both tokens to get the combined one, you must put some trust in whoever you’re giving your token to. You can, of course, just give up your rights to drawing pixels and give your token to somebody else. Perhaps you can work out some kind of deal with your friend (I don’t know how much a pixel is “worth”; the whole idea is to encourage collaborative creativity and not make somebody rich but, as I said, I don’t want to control what will happen).

The first rule is interesting, too, as the image fills up and people decide to paint over other people’s work. Perhaps there are parts of the image that don’t fit aesthetically with the rest. Perhaps someone went all personal on somebody else (what’s the least number of pixels to draw a Polish joke?). Or, the canvas is full but people still have tokens left.

To make it a fun and lasting exercise I’ll be giving tokens to people going forward (not just in the initial batch), perhaps for commenting on this blog? (ah yes, brownie points for participation…)

You can check out the work in progress here. Unfortunately, I can’t spend a lot of time trying to make the site work well in all browsers, so I had to focus on those that are standards-compliant (biggest bang for the buck). Consequently, this page won’t work if you’re using IE6. I’m not sure about IE7 or IE8. I have tested it on Safari and Firefox.

If I haven’t sent you an email with a token, let me know (me at elevenseconds dot-you-know-what) and I’ll throw something in.

Playing with Color (part II)

Saturday, June 20th, 2009

Last time we took an image that displayed a message which would disappear when the image was rendered in grayscale. A much more interesting problem is the opposite one — can we embed a message an in image so that it appears when the image is rendered in grayscale?

It’s not as easy as the first problem — there we had information theory on our side (the conversion to grayscale destroys information as three components — red, green and blue — get converted to just one — intensity). Now we have to get a little more creative (again, you should be able to improve on my proposed algorithm).

Instead of using information theory, we’re going to use psychology: we’re going to trick the brain.

The idea is pretty simple, but it requires us to think in the HSL space: instead of thinking of color as a composite of three primary colors, think of a color as a composite of three properties: its hue (“where on the rainbow the color is”), its saturation (“how vivid the color is”) and its lightness (“how close is the color to white”). Lightness is not quite the same as intensity (because hue and saturation are nonlinear in the red, green and blue components). It turns out that our brain decomposes the colors into their HSL components better than their RGB components (which one of the two colors has a higher R component, this one or this one?). The idea is to encode the message with a color of a lower intensity that compensates for it by having an unusually high lightness.

First, we’ll define a scratchpad — a two-dimensional set of all possible colors of a given intensity I (I used I=0.3 experimentally). The color space is three-dimensional, but if we add one constraint (intensity has to be equal to I) we can map the resulting colors onto a two-dimensional space. We will use the x-coordinate to determine the intensity of the green component and the y-coordinate — of the red component. We would like the pixel color to be uniquely identified by these two values. This is often possible, but if the green and red intensities are outside of certain bounds such a color won’t be possible (for example, consider a pixel whose green and red components are both 0.5 — if you want the pixel to have an intensity of 0.3, the blue component would have to be equal to -1.25).

Hence, we’ll transform the x and y coordinates so that the resulting image is rectangular — that is, for each row, x=0 will map to the smallest value of the green component that makes a color possible, and x=max will map to the largest value of the green components that makes a color possible.

If we didn’t transform the coordinates, the resulting scratchpad would look like this — note the areas that make it impossible to construct a color:

All possible colors of intensity 0.3

All possible colors of intensity 0.3

Now transform the coordinates (this gets rid of the empty space and expands the rest). Click on the image to cycle through the scratchpad, its lightness map, and its intensity map (the latter should be all homogenous color, equal to I)

An expanded version of the above set of colors

An expanded version of the above set of colors

We’ll take advantage of two properties of such a scratchpad: every pixel has the same intensity; and pixels that are close are of similar colors (since the mapping is continuous in the red — as we move up and down the scratchpad — green — as we move left to right — and blue space — because blue is derived from the intensity formula, which is linear).

Now go through the scratchpad and find pixels that have unusually high lightness values and make them darker. By changing the lightness we’ll be changing the intensity of the pixel but because these pixels have high lightness our brain won’t be able to determine the difference in intensities. I use a scaling function with a gamma correction:


if(lightness>I)
lightness = ( ( (lightness-I)/0.35 )^0.8 )*0.2 + I
end

Click on the image to cycle through the scratchpad, its lightness map and its intensity map (this time the latter should have a darker area):

We've altered some pixels to lower their lightness

We've altered some pixels to lower their lightness

Now, if you look at the resulting image, you will see that a number of pixels have an intensity different from I but they still look pretty bright (because their lightness is high). We’ll pick either the dark or the bright pixels based on whether we’re encoding the message or the background. We limit the colors we pick to a subset of the scratchpad that features pixel ranges with the highest changes in intensities (that way we can “smuggle” a low-intensity pixel in around similar pixels of a higher intensity).

Secret message embedded using pixels of lower intensity but unusually high lightness

Secret message embedded using pixels of lower intensity but unusually high lightness

We can stop here — transform the input image the same way we did it last time, by decreasing the variance between white and black and pick a color from the scratchpad that matches the intensity. But this is not good enough (our brain is, after all, smart). Let’s introduce a distraction message that appears faintly in the color version of the image but disappears in the grayscale version (we already know how to do that!). Based on the intensity of the respective pixel in the distraction image we’ll alter the color we picked from the scratchpad without altering its intensity (so as to cause no difference in the grayscale image).

Added a distraction image

Added a distraction image

We’re done — we’ve successfully tricked our brain (provided that we pick a smart distraction message).

Note that on your computer, you will still be able to see the message in the image above, for two reasons.

  • Depending on which browser (!) you’re running, your browser may embed color profiles in images to compensate for differences that various computer screens render colors. This, of course, screws our mathematics up somewhat.
  • If you’re viewing this on an LCD screen (and, let’s face it, you are), you will be able to change the angle from which you look at the screen to reveal the hidden message.

If we print the image out, however, we’ll see the full effect. To fix it so that the message disappears on a computer screen as well, we can simply apply the inverse color profile transformation, or define the region of the scratchpad that we pick pixels from differently.

Enter some short text here:

Enter the distraction text here (ideally same length as the message above):

and then click this link

Source:
disappearing-helper.rb

Good reading:
Colour Space Conversions

Playing with Color (part I)

Friday, June 12th, 2009

I’m fascinated by color. When I was younger I dreamt of discovering a color that nobody has ever seen before. And since most computer languages give you easy ways to manipulate graphics, I played with color on a computer.

One day I saw an interesting effect on some certificate I got. When you photocopied it in black and white, a word appeared in the photocopy that wasn’t visible in the original that said “COPY”. In fact, this effect took advantage of the photocopier’s inability to faithfully replicate the document (in this case the word COPY was visible in the original but it was difficult to see because it consisted of a bunch of tiny dots), but I wondered whether it would be possible to hide messages in documents even if the copier were to produce faithful facsimiles.

Specifically, I asked myself, can I come up with a message that is visible on a color version of an image but that disappears on the grayscale version? It turns out that it’s possible, and not that difficult. I’ll explain how, while trying to limit distracting you with color theory to the minimum.

On a computer screen, each color is a mixture of three primary colors, red, green and blue (printers use a slightly different scheme but for all intents and purposes we can limit ourselves to talking about the RGB space). It turns out that pure red does not appear as “bright” to the eye as pure green. In fact, the intensity of a color (how “bright” it appears to the eye regardless of its hue) has been found to be a weighed average of the three primary components:

intensity = 0.299*red + 0.587*green + 0.114*blue

This intensity is precisely what you see when you convert an image to grayscale (note that you may find different formulas depending on what devices you’re dealing with — different devices have different sensitivity to various primary colors). For example, consider the following image and its grayscale version:

Not all colors were created equally bright

Not all colors were created equally bright

(Of course your favorite graphics editing software simply applies the above transformation; to find the precise relation from first principles you can take a picture of the image below with an old-fashioned analog camera on a black-and-white film and find rectangles that match in intensity).

Use this to derive the intensity formula

Use this to derive the intensity formula

A simple idea for hiding messages is therefore to produce an image of pixels whose intensity is all the same, but whose hue is different (so that we can see the message in color). For example, we can give every pixel of the message the color of (1, 0.3, 0.4) and every pixel of the background the color of (0.3, 0.7, 0.176). The intensity of the former is 0.52, as is the intensity of the latter. Since the intensities are close to each other, when we desaturate the image, the message will blend with the background.

This works well if we know the relationship for a given device exactly. Some printers (if we wish to print the image out) use a slightly different relationship (or perhaps they round the weights). In such a case the two intensities will not be exactly the same and the eye is surprisingly good at telling the difference between two close colors if they cover large areas! We can improve this algorithm by adding some randomness (this is just a suggestion that works pretty well, you can find much better algorithms!):

Consider an input image that contains the message (white text on black background). Each pixel has an intensity of 0 (if the pixel belongs to the background) or 1 (if the pixel belongs to the text), and probably somewhere in between if the text is antialiased.

The secret message we'll try to hide

The secret message we'll try to hide

First, transform the image by adding some random noise and reducing the variance between white and black:

targetIntensity = (rand() + sourceIntensity + 1)*0.33

Adding some randomness

Adding some randomness

Once we have the target intensity, we’ll randomly adjust the intensity formula and find an output color such that the intensity formula cancels targetIntensity out:


alpha = 0.587 + (rand(25)-12)*0.001
beta = 0.299 + (rand(25)-12)*0.001
gamma = 1.0 - alpha - beta

ratio = (alpha-gamma)/beta
r = 0.44 + targetIntensity/3.0
g = 0.62 - targetIntensity*ratio/3.0
b = 0.9 - targetIntensity/3.0

Convince yourself that the intensity is a constant, independent of targetIntensity. But since targetIntensity varies, the color version of the image will consist of distinctly different pixels.

As a nice touch we can alternate between two different formulas for added noise:

The resulting message that disappears when rendered in grayscale!

The resulting message that disappears when rendered in grayscale!

Print the above image out in black and white and see the message disappear!

Of course, it would be great if we could write a program that takes a message and displays an image like the one above. Ruby with RMagick makes it very easy:

Enter some short text here:

and then click this link

Source:
disappearing-helper.rb

Good reading:
Colour Space Conversions