At 11:24 -0600 03/14/2002, I wrote:

[re noise filter]

>This is a crude example, though. The early incarnations don't write 
>to LSBs for the pixels and you can see the image has been altered in 
>some way. I have a *much* sleeker version that will in fact write to 
>LSBs -- it even does conversion of decimal to 32-bit binary and back 
>again -- and if you want I can send along the file as it is now. 
>It's slow and wonky -- could use all kinds of optimization

And actually I just realized how to optimize it. I don't need to do a 
dec-to-binary conversion for the RGB data to write to the LSB using 
iLingo. That represents a vast speed improvement.

In binary the last position in a number always corresponds to the 
integer 1, and all digits to the *left* are doubles of the one 
immediately preceding.

This means that if the final bit in a nonzero binary number is 1, the 
decimal value of that number will *always* be odd. There can be no 
exception. Similarly, if the last bit is set to 0, the decimal number 
will *always* be even.

Well, the blue channel in an RGB color (at least in Director) 
represents the least significant *channel* when handling its value. 
Thus any time you have a color you can take its colorValue.blue and 
end up with an 8-bit number that represents the LSB for the color. 
This is because the colors are ( red * 256 * 256 ) + ( green * 256 ) 
+ blue for the decimal version of the RGB color; hence the blue 
channel represents the one holding the least amount of information 
that is represented in the RGB color. The blue channel will always be 
an integer ranging from 0 to 255 inclusive. (They all are, but 
remember there are multipliers for g and r!)

Why this is interesting is you can then use a modulus test to 
determine if this integer is odd or even, and then modify it to 
represent your binary stream.

OK, let's say you have a ten-char message in ASCII. You do a 
charToNum on each one to get the integer value for each char. You 
then convert that simple stream into eight-bit binary numbers, which 
overall goes pretty damn fast. (Much faster, anyway, than taking a 
300 by 200 image at 24+ bits and converting *that*.)

Once you have your bitstream, you go through it a char at a time and 
twiddle the values for each blue channel in successive pixels in your 
image. If your bit is 0, you set the image pixel's value to an even 
number (from, for instance, 33 to 32). If your bit is 1, you set the 
image pixel value to an odd number (32 --> 33, or even 32 --> 31; you 
just need to look out for 0 and 255 and be sure you don't go beyond 
them). If the value for the pixel is already where it should be -- 
and there's an averaged 50 percent chance that will happen for each 
bit you are embedding -- you leave it alone.

Then you feed that data back into the image. You've just done LSB 
modifying without having to convert the entire pixel value into 
binary.

You end up diddling 80 pixels that way. All the rest you can ignore 
or, if you want to allow variable length messages, you could write a 
specific block of bits to indicate the end of the file, say eight 0s. 
Heh, ASCII null, which is in fact an EOF marker.

Reconstructing the message would then be cake, because all you need 
is the modulus of each pixel's blue value passed through 2. If the 
blue value is even the modulus would be 0, and if the value is odd 
the mod is 1. That's your binary bitstream. Keep going until you have 
your eight 0s (your EOF marker). Take it in 8-bit chunks, convert it 
to decimal and switch that back into text with numToChar.

What is also nice about this is it lets you salt your data randomly 
in the image itself, as I discuss online in the NF section. It's a 
little anal, of course, but it can help make it that much harder to 
see if an image has been used to transmit text or anything else via 
steg. (There are programs now that can detect commonly used stego 
routines in images, most of which I assume begin writing at either 
the first or last bit in an image and proceed linearly from there.)

And of course what you do to the original message to take it out of 
plaintext to begin with is also up to you.

Some of this is getting off the topic of Chris's original question, 
but suppose you take the LSBs of the first 32 pixels in the image and 
write them out as a binary value using the method above. You would 
*probably* have a random stream of 1s and 0s. Convert it to an 
integer, a 32-bit value.

You could then use that to xOr the RGB values for the rest of the image:

   colorVal = getPixel ( someMember.image, x, y, #integer )
   newVal = bitXOr ( yourPixel, colorVal )
   someMember.image.draw( point ( x, y ), newVal )

...the above is rather untested, mind you.

What you'd get would probably be visual garbage.

You then write *that single 32-bit number* to a pixel -- you'd have 
to sacrifice the data in this one pixel to do this -- whose location 
is determined by setting the randomSeed equal to (for instance) the 
image h * its width. Something reproducible, because of course random 
numbers in computing aren't if they're algorithmically derived. You 
bang your 32-bit pixel into that position and write the file:

   the randomSeed = someMember.image.width * someMember.image.height
   nLocH = random ( someMember.image.width )
   nLocV = random ( someMember.image.height )
   someMember.image.draw( point ( nLocH, nLocV, yourPixel )

That would be your pseudorandom location for the xOr key pixel, and 
every time the h or w of the image was different its location would 
be different -- but the thing is that you can *recover* its location 
in reading back the image by going through the same procedure, first 
setting the randomSeed and then getting the H and V coords to find 
your xOr pixel:

   the randomSeed = someMember.image.width * someMember.image.height
   nLocH = random ( someMember.image.width )
   nLocV = random ( someMember.image.height )
   yourPixel = getPixel ( someMember.image, x, y, #integer )

You then pass the image through the xOr filter a second time and 
reconstruct the whole file, with the one exception of the pixel you 
sacrificed to store the key.

And actually you could even determine beforehand which pixel was 
going to get hosed, and write its value out to the LSBs of 32 other 
pixels in the image. Then you xOr the rest as before, and go ahead 
and write the key into the pixel as before. Reconstructing is just 
the reverse of the process, but when you're done you go to those 32 
"restore" pixels, read their LABs, and then feed that value back into 
the "key" pixel, resetting it to its pristine state before you wrote 
your key into it.

This means, of course, that 32 pixels in the image will not be 
precisely the same as when you started, but the differences will be 
visually indistinguishable. Literally -- the human eye can only 
discern about 4 to 5 million shades, so this kind of variation is 
nothing.

That gives you a file format that's totally valid -- BMP for instance 
-- but which, opened in anything but your program, should show 
nothing but garbage. (Though I wonder if it wouldn't just turn into 
something that looks like you did an add pin color blend to it!)

-- 

              Warren Ockrassa | http://www.nightwares.com/
  Director help | Free files | Sample chapters | Freelance | Consulting
        Author | Director 8.5 Shockwave Studio: A Beginner's Guide
                    Published by Osborne/McGraw-Hill
          http://www.osborne.com/indexes/beginners_guides.shtml
[To remove yourself from this list, or to change to digest mode, go to 
http://www.penworks.com/lingo-l.cgi  To post messages to the list, email 
[EMAIL PROTECTED]  (Problems, email [EMAIL PROTECTED]). Lingo-L is for 
learning and helping with programming Lingo.  Thanks!]

Reply via email to