Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Matt Graf

Embedding Secret Messages In An Image Using ColdFusion

By Ben Nadel on
Tags: ColdFusion

After pixelating a ColdFusion image using the underlying BufferedImage Java object, I wanted to see what other kinds of fun I could have playing with RGB values. One thing I thought would be kind of cool would be to embed character data in the image data. Each character can be translated into a numeric ASCII value; as such, I figured I could use that numeric value equivalent to fudge the RGB value of a given pixel.

My first approach to this problem was to embed a single character in each pixel, distributing the ASCII value across the three color channels - Red, Green, and Blue. The problem with this approach, though, is that I wanted to support up to the ASCII value 255 (the standard set), which can drastically shift the RGB value of a given pixel (not to mention it was very hard to figure out how much of that ASCII value to put in each color channel).

My second approach - the one I finally went with - was to take the ASCII concept one step further and convert each ASCII value to its bit value equivalent (ASCII in base2). Then, rather than trying to stuff a single character into a single pixel, I would only stuff a single bit into a single pixel. This meant I needed 8 pixels to store a single character (8 bits to a character); but, a single bit could easily be stored in a single pixel without any visual distortion.

As an example, let's say I have the letter, "H". The ASCII value of this is 72. 72 in base2 (bit version) is "01001000". Breaking that bit string into individual numbers, I then needed 8 pixels, each of which would be altered by one, or left alone (if given bit is zero).

Once the pixels of a given image are altered based on the given character data, there is no way to extract the character data from the mutated picture alone. Both the person embedding the text and the person extracting the text would need to have a copy of the original image. Then, and only then, can the embedded character data be extracted by comparing the two images (the original and the mutated), creating bit strings based on the difference. These bit strings could then be converted back into ASCII and, subsequently, back into printable characters.

Before we look at the ColdFusion component that does this, ImageMessage.cfc, let's take a look at a demo to see how it might be used:

  • <!--- Create an instance of the image message component. --->
  • <cfset embedder = createObject( "component", "ImageMessage" ).init() />
  •  
  • <!---
  • Read in the image that will be used as our key - this
  • is the shared image that each user would have to have
  • to extract hidden messages.
  • --->
  • <cfimage
  • name="keyImage"
  • action="read"
  • source="./girl.jpg"
  • />
  •  
  •  
  • <!--- Create a TOP SECRET message to embed. --->
  • <cfsavecontent variable="message">
  •  
  • Katie,
  •  
  • I want to throw a surprise birthday party for Sarah, but
  • it is essential that she does not find out about it! Not
  • only is it her birthday, but I think I am going to propose
  • this year.
  •  
  • Speaking of proposal, I know nothing about picking out a
  • ring. Do you think we could meet this weekend and you can
  • help me out? We can head up to the diamond district to
  • see what's good.
  •  
  • Also, think of some great places to take her out to dinner.
  • I'm just a programmer - I don't know anything about that
  • kind of stuff - my idea of a good time is a Law and Order
  • rerun and take-out Chinese.
  •  
  • Thanks!
  • You're the best!
  •  
  • </cfsavecontent>
  •  
  •  
  • <!---
  • Embed the top secret message in a new image, which will
  • be a dupliate of the Key image.
  • --->
  • <cfset messageImage = embedder.embedMessage(
  • keyImage,
  • trim( message )
  • ) />
  •  
  •  
  • <!---
  • Now that we have our images, let's output them to see if
  • there are any striking visual differences.
  • --->
  • <cfoutput>
  •  
  • <div style="width: 500px">
  •  
  • <h3>
  • KEY Image
  • </h3>
  •  
  • <p>
  • <!--- Key image. --->
  • <cfimage
  • action="writetobrowser"
  • source="#keyImage#"
  • />
  • </p>
  •  
  • <h3>
  • EMBEDDED MESSAGE Image
  • </h3>
  •  
  • <p>
  • <!--- Message image. --->
  • <cfimage
  • action="writetobrowser"
  • source="#messageImage#"
  • />
  • </p>
  •  
  •  
  • <!---
  • Now, let's extract the message from our message image
  • and output it to the screen.
  • --->
  • <h3>
  • EXTRACTED Message (from Message Image)
  • </h3>
  •  
  • <p>
  • <!---
  • Notice that when I extract the message, I need
  • the original KEY image as well as the image that
  • contains the message.
  • --->
  • #replace(
  • embedder.extractMessage(
  • keyImage,
  • messageImage
  • ),
  • chr( 13 ),
  • "<br />",
  • "all"
  • )#
  • </p>
  •  
  • </div>
  •  
  • </cfoutput>

As you can see in the above code, we are embedding text in an image, then outputting the two images (the original and the mutated) to see if there are any visual differences. Then, using the original image, we extract the message from the mutated image and display the message on the screen. And, in fact, when we run the above code, we get the following page output:

 
 
 
 
 
 
Embedding Secret Messages In ColdFusion Images Using The Underlying Pixel Data. 
 
 
 

As you can see, the two images look exactly alike; and, without any visual distortion, the embedded message was easily extracted.

Ok, now that you see how this works, here is the ColdFusion component that provided the embedding and extracting algorithms:

ImageMessage.cfc

  • <cfcomponent
  • output="false"
  • hint="I provide functionality to embed and extract messages within a given image based on a shared key image.">
  •  
  •  
  • <cffunction
  • name="init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return an intialized component.">
  •  
  • <!--- Return this object reference. --->
  • <cfreturn this />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="copyImage"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I duplicate the given image.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="image"
  • type="any"
  • required="true"
  • hint="I am the image being duplicated."
  • />
  •  
  • <!--- Copy the image and return it. --->
  • <cfreturn imageCopy(
  • arguments.image,
  • 0,
  • 0,
  • imageGetWidth( arguments.image ),
  • imageGetHeight( arguments.image )
  • ) />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="embedMessage"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I embed the given message in a duplicate of the give shared image key and return the message image.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="keyImage"
  • type="any"
  • required="true"
  • hint="I am the image key (the shared image) that will be used to create the message image."
  • />
  •  
  • <cfargument
  • name="message"
  • type="string"
  • required="true"
  • hint="I am the message being embedded in the image key (a duplicate of the image key)."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • Duplicate the image key (since we don't want to alter
  • the key itself but rather work based off of it).
  • --->
  • <cfset local.image = this.copyImage( arguments.keyImage ) />
  •  
  • <!--- Get the width and height of the image. --->
  • <cfset local.imageWidth = imageGetWidth( local.image ) />
  • <cfset local.imageHeight = imageGetHeight( local.image ) />
  •  
  • <!---
  • Check to see if this image is large enough to hold the
  • given message. Each character will require 8 pixels -
  • we are going to store one ASCII BIT per pixel (we need
  • 8 bits to hold one character).
  • --->
  • <cfif (
  • (len( arguments.message ) * 8) gt
  • (local.imageWidth * local.imageHeight)
  • )>
  •  
  • <!--- Throw message length error. --->
  • <cfthrow
  • type="MessageLength"
  • message="The message you are trying to embed is too long for this image."
  • detail="The message you are trying to embed is of length [#len( arguments.message )#] and the given image key can only accept messages of max length [#fix( local.imageWidth * local.imageHeight / 8 )#]."
  • />
  •  
  • </cfif>
  •  
  • <!---
  • Get the underlying buffered image. This will give
  • use access to all of the underlying pixel data for our
  • "message" image.
  • --->
  • <cfset local.bufferedImage = imageGetBufferedImage(
  • local.image
  • ) />
  •  
  • <!---
  • Figure out the number of pixel rows that will be
  • required to embed the message.
  • --->
  • <cfset local.requiredRows = ceiling(
  • len( arguments.message ) * 8 / local.imageWidth
  • ) />
  •  
  • <!---
  • Get enough RGB pixels to embed the message based on
  • the given rows.
  • --->
  • <cfset local.pixelBuffer = local.bufferedImage.getRGB(
  • javaCast( "int", 0 ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.imageWidth ),
  • javaCast( "int", local.requiredRows ),
  • javaCast( "null", "" ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.imageWidth )
  • ) />
  •  
  • <!---
  • Now that we have our pixel buffer, we need to create
  • our own into which we can store the "message" pixels.
  •  
  • NOTE: We can convert this back to a Java array when
  • we overwrite the pixel buffer.
  • --->
  • <cfset local.messagePixelBuffer = [] />
  •  
  • <!---
  • Resize to be the size of the original pixel buffer -
  • this will be faster to resize upfront than to later
  • set values by appending them as needed.
  • --->
  • <cfset arrayResize(
  • local.messagePixelBuffer,
  • arrayLen( local.pixelBuffer )
  • ) />
  •  
  • <!---
  • Now, let's split the message up into an array so
  • that we can easily access each of the characters.
  • --->
  • <cfset local.characters = reMatch(
  • "[\w\W]",
  • arguments.message
  • ) />
  •  
  • <!---
  • We are going to loop over the characters to embed
  • them in the pixel colors. We will need 8 pixles (one
  • per bit) to embed a single ascii character. Therefore
  • we need to keep a seperate index for the pixel buffer
  • than we do for the characters.
  • --->
  • <cfset local.pixelIndex = 1 />
  •  
  • <!--- Loop over the characters. --->
  • <cfloop
  • index="local.character"
  • array="#local.characters#">
  •  
  • <!--- Convert the character to it's ASCII value. --->
  • <cfset local.characterAscii = asc( local.character ) />
  •  
  • <!---
  • Make sure the ASCII value is not greater than 255.
  • If it is, we are going to replace it with the "?"
  • mark to indicate that this is beyond the
  • capabilities of the embedding.
  • --->
  • <cfif (local.characterAscii gt 255)>
  •  
  • <!--- Overwrite with "?" ascii. --->
  • <cfset local.characterAscii = 63 />
  •  
  • </cfif>
  •  
  • <!--- Convert the charater to a array of bits. --->
  • <cfset local.characaterBits = reMatch(
  • ".",
  • formatBaseN( local.characterAscii, 2 )
  • ) />
  •  
  • <!--- Make sure the bit array is 8 bits. --->
  • <cfloop condition="arrayLen( local.characaterBits ) lt 8">
  •  
  • <!--- Prepend a zero. --->
  • <cfset arrayPrepend(
  • local.characaterBits,
  • "0"
  • ) />
  •  
  • </cfloop>
  •  
  • <!---
  • Loop over the array bit array and use each bit to
  • update the pixel color.
  • --->
  • <cfloop
  • index="local.characterBit"
  • array="#local.characaterBits#">
  •  
  • <!---
  • Check to see if the current pixel value is
  • positive or negative. To be on the safe side,
  • we are goint to move towards zero.
  • --->
  • <cfif local.pixelBuffer[ local.pixelIndex ]>
  •  
  • <!--- Positive value, so subtract. --->
  • <cfset local.messagePixelBuffer[ local.pixelIndex ] = (local.pixelBuffer[ local.pixelIndex ] - local.characterBit) />
  •  
  • <cfelse>
  •  
  • <!--- Negative value, so add. --->
  • <cfset local.messagePixelBuffer[ local.pixelIndex ] = (local.pixelBuffer[ local.pixelIndex ] + local.characterBit) />
  •  
  • </cfif>
  •  
  • <!--- Increment the pixel index. --->
  • <cfset local.pixelIndex++ />
  •  
  • </cfloop>
  •  
  • </cfloop>
  •  
  • <!---
  • Now that we have copied over the characters, copy
  • over the rest of the pixels (which aren't being used
  • to embed character data).
  • --->
  • <cfloop
  • index="local.pixelIndex"
  • from="#local.pixelIndex#"
  • to="#arrayLen( local.pixelBuffer )#"
  • step="1">
  •  
  • <!--- Copy existing color. --->
  • <cfset local.messagePixelBuffer[ local.pixelIndex ] = local.pixelBuffer[ local.pixelIndex ] />
  •  
  • </cfloop>
  •  
  • <!---
  • Now, let's write the pixel buffer BACK into the
  • message image.
  • --->
  • <cfset local.bufferedImage.setRGB(
  • javaCast( "int", 0 ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.imageWidth ),
  • javaCast( "int", local.requiredRows ),
  • javaCast( "int[]", local.messagePixelBuffer ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.imageWidth )
  • ) />
  •  
  • <!--- Return the message image. --->
  • <cfreturn local.image />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="extractMessage"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="I extract an embedded message from the given image based on the shared key image.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="keyImage"
  • type="any"
  • required="true"
  • hint="I am the image key (the shared image) that will be used to extract the embedded message."
  • />
  •  
  • <cfargument
  • name="messageImage"
  • type="any"
  • required="true"
  • hint="I am the image with the embedded image."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!--- Get the key image dimentions. --->
  • <cfset local.keyImageWidth = imageGetWidth( arguments.keyImage ) />
  • <cfset local.keyImageHeight = imageGetHeight( arguments.keyImage ) />
  •  
  • <!--- Get the message image dimentions. --->
  • <cfset local.messageImageWidth = imageGetWidth( arguments.messageImage ) />
  • <cfset local.messageImageHeight = imageGetHeight( arguments.messageImage ) />
  •  
  • <!---
  • Check to make sure the images have the same dimensions.
  • If not, then correct key has not been supplied (not to
  • mention we might get OutOfBounds errors when trying to
  • extract the message).
  • --->
  • <cfif !(
  • (local.keyImageWidth eq local.messageImageWidth) &&
  • (local.keyImageHeight eq local.messageImageHeight)
  • )>
  •  
  • <cfthrow
  • type="IncorrectKeyImage"
  • message="The key image you provided does not have the same dimensions as your message image."
  • />
  •  
  • </cfif>
  •  
  • <!---
  • Get the pixels from the key image. Because we don't
  • know how large the message is, we'll just get all of
  • the pixels.
  • --->
  • <cfset local.keyPixels = imageGetBufferedImage(
  • arguments.keyImage
  • )
  • .getRGB(
  • javaCast( "int", 0 ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.keyImageWidth ),
  • javaCast( "int", local.keyImageHeight ),
  • javaCast( "null", "" ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.keyImageWidth )
  • )
  • />
  •  
  • <!---
  • Get the pixels from the message image. Because we
  • don't know how loarge the message is, we'll just get
  • all of the pixels.
  • --->
  • <cfset local.messagePixels = imageGetBufferedImage(
  • arguments.messageImage
  • )
  • .getRGB(
  • javaCast( "int", 0 ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.messageImageWidth ),
  • javaCast( "int", local.messageImageHeight ),
  • javaCast( "null", "" ),
  • javaCast( "int", 0 ),
  • javaCast( "int", local.messageImageWidth )
  • )
  • />
  •  
  • <!---
  • Create a message character array. As we find each
  • character, we will append it to this array (to later
  • be turned into a full string).
  • --->
  • <cfset local.characters = [] />
  •  
  • <!---
  • Create a bit array to hold the differnce between
  • each pixel.
  • --->
  • <cfset local.characterBits = [] />
  •  
  • <!---
  • Loop over the pixel buffer to start adding pixel
  • differences to the bit array.
  • --->
  • <cfloop
  • index="local.pixelIndex"
  • from="1"
  • to="#arrayLen( local.messagePixels )#"
  • step="1">
  •  
  • <!---
  • Append the difference the character bits. Be sure
  • to use the absolute values since the alpha channel
  • can give us odds values.
  • --->
  • <cfset arrayAppend(
  • local.characterBits,
  • abs(
  • abs( local.keyPixels[ local.pixelIndex ] ) -
  • abs( local.messagePixels[ local.pixelIndex ] )
  • )
  • ) />
  •  
  • <!---
  • Check to see if our bit array is of lenth 8. If
  • it is, then we have collected an entire character
  • which we can then convert to CHR and append to the
  • message array.
  • --->
  • <cfif (arrayLen( local.characterBits ) eq 8)>
  •  
  • <!--- Convert to ASCII value. --->
  • <cfset local.characterAscii = inputBaseN(
  • arrayToList( local.characterBits, "" ),
  • 2
  • ) />
  •  
  • <!---
  • Check to see if the ASCII value is zero. This
  • would result if there was no difference
  • between the two pixels; this will signal the
  • end of the string.
  • --->
  • <cfif !local.characterAscii>
  •  
  • <!--- We are done. Break out of loop! --->
  • <cfbreak />
  • <!--- ------------------------------- --->
  • <!--- ------------------------------- --->
  •  
  • </cfif>
  •  
  • <!--- ASSERT: We are still matching. --->
  •  
  • <!---
  • Append character to building message character
  • array. When we do this, we have to convert the
  • ASCII value to a printable character.
  • --->
  • <cfset arrayAppend(
  • local.characters,
  • chr( local.characterAscii )
  • ) />
  •  
  • <!---
  • Reset the bit array (so that we can start
  • building the next character).
  • --->
  • <cfset local.characterBits = [] />
  •  
  • </cfif>
  •  
  • </cfloop>
  •  
  • <!---
  • Join the character array as a single string and
  • return it.
  • --->
  • <cfreturn arrayToList( local.characters, "" ) />
  • </cffunction>
  •  
  • </cfcomponent>

I know that if you are already relying on shared, private keys, there are far more efficient ways to send encrypted messages (such as with straight up text values); but, I just thought this was a lot of fun. Plus, you could have a large set of shared keys that would have to be matched up visually, rather than just using the same key for each value. And of course, an image is much less conspicuous than a chunk of garbled text.

Anyway, I just thought this was a really fun way to end the week.




Reader Comments

@Raymond,

Oh snap - I didn't even think of it for this. But yeah, I guess that makes sense. This weekend :)

Reply to this Comment

Quite frankly you could encrypt the message with almost any encryption algorithm, then embed the encrypted data in the image(if the result isn't too large), which would give you the security of the encryption used with the inconspicuousness of an image. If the message data bit length is too long you could always encode the data 1 bit per channel which would be 3 bits per pixel, 4 bits per pixel if the format supports alpha channel.

Reply to this Comment

I had to try it, so I modified your code to encode 1 bit per color channel(3 bits per pixel) and to encrypt the data before embedding.

I am using bit shifting and XOR to embed and extract the data bits.

I did have an error with your original code and the image I borrowed of you and Laura Arguello:
"
Invalid argument for function InputBaseN.
The argument 1 of InputBaseN which is now 000016777215101 must be a valid number in base 2.
"
.

16777215=11111111 11111111 11111111=white
I think it might be an integer rollover issue

http://www.drm31415.com/embed.txt
http://www.drm31415.com/ImageMessage.txt

Is it just me or does the image with embedded data seem lighter in both examples?

Reply to this Comment

Now for even more fun!

I updated yet again to instead of embedding ASCII text embed another picture

http://www.drm31415.com/embed2.txt
http://www.drm31415.com/ImageMessage2.txt

I have a pair of images, see if you can find whats embedded
http://www.drm31415.com/laura_arguello.jpg < Key
http://www.drm31415.com/secret.png < Hidden Image

The embedded is lead by an embedded 32bit int storing the width in the higher 16 bits and the height in the lower 16 bits.

Because a pixel is stored as an 32bit int with 3 channels( I don't believe the upper 8bits are usable because RGB doesn't use it so (get/set)RGB() may not retrieve/set it ) to store a 80x80 image it takes a image with ceiling(((80*80)+1)*32/3)=68278 pixels to store it which is just over 10 times the size, the +1 is to store the dimensions

This technique could be adjusted to store virtually any type of binary data, as long as you had enough room in the key image or used multiple result images and split the embedded data.

The storage could be doubled by using 3(00000011 in binary) as MessageBitMask and Shifting in 2's but it would increase the visual impact in the result.

The embedded image could also be encrypted before embedding like the 1st example.

PS: I can take down the laura_arguello.jpg whenever you want, just wanted to use a recognizable image.

Reply to this Comment

@David,

That is very cool! I see you hit my Gravatar thumbnail :)

Using the bit manipulation is definitely clever (using one bit per channel), but it certainly ups the complexity of the algorithm a lot! I had a bit of trouble following it (bit manipulation is one thing I don't visualize very well), but I ran it and it works well!

Very cool experimentation :)

Reply to this Comment

I could have commented it better, yours simply adds the the lowest bit which is presumable the lowest bit in the blue channel.
I pluck out the bits out of the int/ascii by shifting it so the bit I want is the rightmost/lowest order then Bitwise anding it with 1(binary 00000001) so only the rightmost bit is reflected in the result then I store it in an int with the lower 3 bytes corresponding to the 3 color channels.
I use an int to store the red bit, shift it 8 bits to the left, store the green bit, shift it 8 bits to the left, store the blue bit, store int in array, clear int to 0, start over, the resulting int is all zeros except for the lowest bit in the final 3 bytes.

Then it Xors the ints together with the corresponding pixel ints.
Bitwise XOR is cool because:
If IntA is a Key Int and IntB is a Data Int and IntC is the XOR result of IntA and IntB then the XOR result of IntA and IntC is IntB, so with the Key and Result it is easy to retrieve the embedded data.

Reply to this Comment

@David,

Bit manipulation is something that I understand at the conceptual level, but it's just hard for me to think that way since I rarely use bits. In fact, when I first started the approach to store each letter inside a single pixel, I got to playing with the BitSHLN() and BitSHRN() methods. But, that said, it definitely trips me up to think in bits :)

Reply to this Comment

@David,

Getting some of those finger configurations to work kind of strains my hand. Nine, for example, feels crazy awkward.

Reply to this Comment

20(10100) seems the most difficult to me, btw avoid 5(00101) or 640(1010000000) or even worse 645.
Today is a binary date btw, 01/10/10, yes I am using a 2-digit year, sue me :)

Reply to this Comment

Ben, As I was sitting here reviewing your code I found myself wondering how to do something similar. I imagined doing something along these lines: As you type each letter it is simply replaced with a single pixel of color. So if you type the letter 'a' it would map to a blue pixel (or perhaps a green 2x2 pixel.) The output wouldn't necessarily make sense but it could turn out pretty with a few paragraphs... (:

Reply to this Comment

@David,

Nothing wrong with a 2-year date, when done for a cool purpose ;)

@Kristopher,

It's funny you mention that because I used to wonder what "music" would look like as an image, much in the same way - it's all just "data".

Reply to this Comment

I modified the code to make 'pictures' from binary data then re-extract it, this was a learning experience because to rebuild the bytearray I had to extract the separate bytes and convert them to signed byte values to make java happy. It can be VERY memory intensive depending on what you use so be careful, It can't use files more than approx 16MB(just under).

I noticed on the few mp3s I used the resulting pics had stripes.

http://www.drm31415.com/embed3.txt
http://www.drm31415.com/ImageMessage3.txt

Reply to this Comment

@David,

What kind of image does this create? Just something with completely random looking colors?

There's got to be a easier way to work with bits. I'm not sure what I don't like about it yet, but just there's got to be a nicer "wrapper" for this kind of functionality.

I'm gonna put by bit-thinking cap on (which is really small at this time).

Reply to this Comment

Speckly images, although some interesting patterns developed in the mp3 ones, may try on a small mpg or avi to see if it is the same.

Reply to this Comment

I used to work at the UCF Vision lab... My job was to convert C++ and MatLab code into OpenGL/GLSL code so it could be processed on the GPU (Graphical processing unit). The only way to store the information was in matrices (which were image textures). This is very useful stuff... to someone

(The algorithms we ran on the GPU were 1000x faster than when ran on CPU)

Reply to this Comment

@Tommy,

I am not really sure what you just said :) Sorry, a bit above my head. I remember matrices, though, from finite math.

Reply to this Comment

Save Anna Chapman! Save Anna Chapman! Save Anna Chapman!

What a babe! It would be such a shame for her to spend the next 20 years in prison. That punishes **US** not the Russians!!

(Like I had a shot.)

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.