Steganography Coder

Visuals, audio, animation. Blended, not stirred. If LiveCode is part of your rich media production toolbox, this is the forum for you.

Moderators: Klaus, FourthWorld, heatherlaine, kevinmiller, robinmiller

Post Reply
Xero
Posts: 77
Joined: Sat Jun 23, 2018 2:22 pm

Steganography Coder

Post by Xero » Mon Oct 26, 2020 4:26 am

Hi people!
Just thought I'd drop this in here for a bit of fun.
I found an article online about encoding images into the RGB channel of other images, and thought that might be a fun little Livecode project to test my skills on.
I couldn't attach the stack here because of size issues of attachments... sorry!
So, here's an image of the setup as well as all the code for the back end. (Now with changes as per advice...)
Stacks/ substacks: Buttons, Hide, Code
Hide Stack has an image called "image"
Code stack has an image called "image" and a field called "Code"
Buttons stack has the following buttons, and their code-

'Load Image' (will load an image that will be encoded INTO

Code: Select all

on mouseup
   NewImage
end mouseup
'Code in R Channel'

Code: Select all

global pChan

on mouseup
   Put "1" into pChan
   codeImage
end mouseup
'Code in G Channel', 'Code in B Channel'
- put 2 for G channel, put 3 for B channel

These will be used as an offset to isolate each individual channel of the image.

'Decode in R Channel'

Code: Select all

Global pChan

on mouseup
   put "1" into pChan
   DecodeImage
end mouseup
'Decode in G Channel', 'Decode in B channel'
- put 2 for G channel, put 3 for B channel

'Save Image'

Code: Select all

on mouseup
   SaveImage
end mouseup
Main Stack Script (Buttons Stack)
Code is annotated to show what it's doing.

Code: Select all

global pChan

on openstack
   open stack "Code"
   open Stack "Hide"
end openstack

on NewImage
   --load image from harddrive--
   answer files "Select the image you wish to load:" with type "JPEG Images|jpg|JPEG"
   --change the referenced image to new image--
   set the filename of image "Image" of stack "Hide" to it
   --Allow the stacks to be resized by code only--
   set the resizable of stack "Code" to true
   set the resizable of stack "Hide" to true
   --track the w and h of image for scaling--
   put the width of image "image" of stack "hide" into tWidth
   put the height of image "image" of stack "Hide" into tHeight
   --check dimensions of image fit on screen, if not, resize in scaled manner--
   if tWidth > item 3 of the screenrect then
      set the width of image "image" of stack "Hide" to (item 3 of the screenrect - 100)
      set the height of image "Image" of stack "Hide" to ((width of image "Image" of stack "Hide" / tWidth)*theight)
   end if
   if tHeight > item 4 of the screenrect then
      set the height of image "image" of stack "Hide" to (item 4 of the screenrect - 50)
      set the width of image "Image" of stack "Hide" to ((height of image "Image" of stack "Hide" / tHeight)*twidth)
   end if
   --adjust stack sizes to image size, make sure both stacks are the same size--
   Set the height of stack "Hide" to the height of image "Image" of stack "Hide"
   Set the width of stack "Hide" to the width of image "Image" of stack "Hide"
   Set the height of stack "Code" to the height of image "Image" of stack "Hide"
   Set the width of stack "Code" to the width of image "Image" of stack "Hide"
   --make the text for coding field the same size as the stack---
   Set the height of fld "Code" of stack "Code" to the height of image "Image" of stack "Hide"
   Set the width of fld "Code" of stack "Code" to the width of image "Image" of stack "Hide"
   --make the dummy image the same size as the stacks--
   Set the height of img "Image" of stack "Code" to the height of image "Image" of stack "Hide"
   Set the width of img "Image" of stack "Code" to the width of image "Image" of stack "Hide"
   --locate resized elements--
   set the topleft of image "Image" of stack "Hide" to 0,0
   set the topleft of Field "Code" of stack "Code" to 0,0
   set the topleft of image "Image" of stack "code" to 0,0
   --lock the stack sizes so they stay the same--
   set the resizable of stack "Hide" to false
   set the resizable of stack "Code" to false
   --open the code stack if it isn't already open--
   open stack "Code"
end NewImage

on SaveImage
   --get location on harddrive--
   ask file "Save to?"  with type "JPEG Images|jpg|jpeg"
   put it into tFile
   --save the referenced image--
   put image "image" of stack "Hide" into url ("binfile:" & tFile)
end SaveImage

--Coding based on image channel data being odd or even numbered. 
--If it's odd, it will be shown as white, if it's even, it will be shown as black--
--When encoding, white pixel data that are odd and even pixel data this even will be skipped--

on CodeImage
   go to stack "code" of this stack
   --create jpeg of text field
   export snapshot from card "Code" of stack "Code" to tCodeFile AS JPEG
   --change dummy image to snapshot
   set the TEXT of image "Image" of stack "Code" to tCodeFile
   --check images are the same size, if not, issues will occur so abort--
   if the width of image "Image" of stack "code" = the width of img "Image" of stack "Hide" and the height of img "Image" of stack "code" = the height of image "Image" of stack "Hide" then
      --get the ARGB channel data of both images and put in container--
      put the imagedata of image "Image" of stack "Code" into tCodeFileData
      put the imagedata of image "Image" of stack "Hide" into tHideFileData
      --work out how long the data is so it can be cycled through--
      put the length of tHideFileData into tLength
      --go through each 4th item of the data (i.e. one channel)--
      repeat with i = 1 to tLength step 4
         --look at every 4th character of code + an offset based on which channel is being used. Check if it's black (under 220) or white (over 220)----
         if bytetonum(char i + pChan of tCodeFileData) > 220 then
            --If it's white, check if it's an even number in picture file--
            if (bytetonum(char i + pChan of tHidefileData))/2 is an integer then
               --make it an odd number--
               put (bytetonum(char i + pChan of tHideFileData)) + 1 into tCodePixel
               --change corresponding character in picture file
               put numtobyte(tCodePixel) into byte i + pChan of tHideFileData
            end if
            --Or, if it's black and an odd number, change it to an even number--
         else if bytetonum(char i + pChan of tCodeFileData) <= 220 then
            if (bytetonum(char i + pChan of tHidefileData))/2 is not an integer then
               put (bytetonum(char i + pChan of tHideFileData)) + 1 into tCodePixel
               put numtobyte(tCodePixel) into byte i + pChan of tHideFileData
            end if
         end if
      end repeat
   else
      --if images aren't correct size, inform user--
      answer "Your Images are not the same size"
   end if
   --change the images to suit altered pixels--
   set the imageData of image "Image" of stack "Hide" to tHideFileData
   set the cHiddenImageData of image "Image" of stack "Hide" to tHideFileData
end CodeImage

on DecodeImage
   --Get the channel data of image to be decoded--
   put the imagedata of image "Image" of stack "Hide" into tDecodeFileData
   --get length so they can be cycled through--
   put the length of tDecodeFileData into tLength
   --Go through each 4th pixel--
   repeat with i = 1 to tLength step 4
      --Go through channel by looking at every 4th character + offest for channel. Check if it's odd--
      if bytetonum(char i + pChan of tDecodeFileData)/2 is not an integer then
         --change each channel to be white--
         put numtobyte(255) into byte i +1 of tDecodeFileData
         put numtobyte(255) into byte i +2 of tDecodeFileData
         put numtobyte(255) into byte i +3 of tDecodeFileData
         --If it's even--
      else
         --set the channels to be black--
         put numtobyte(0) into byte i +1 of tDecodeFileData
         put numtobyte(0) into byte i +2 of tDecodeFileData
         put numtobyte(0) into byte i +3 of tDecodeFileData
      end if
   end repeat
   --change the image to show coded text file--
   set the imageData of image "Image" of stack "Hide" to tDecodeFileData
   set the cHiddenImageData of image "Image" of stack "Hide"to tDecodeFileData
end DecodeImage
In short, you enter text in the text field and then it takes a snapshot of the text field in the code stack which will be treated as a black and white image. It checks one of the channels of this code image to see if it's low value i.e. black (0,0,0,) or high value i.e. white (255,255,255) using a high threshold (220), and then changes the relevant coding channel based on the black or white pixel of the code. It changes the value of the channel to be an odd or even number- up or no change, with white being an odd number value, and black being an even number value. This results in an image that will be roughly the same size as the original, and imperceptibly different from the original. The values will only change 1/255th of the colour, so there isn't a noticeable change. There is then 3 spaces to code information, one code for each channel. The decoding process just reads off or even numbers in a channel and converts them to black or white, then presents it.

Was fun to play with, and threw out a good little problem of how to work out if a number is odd or even. I went with asking if the number divided by 2 was an integer (whole number). Only even numbers do that!

I have coded an image resize function so large images won't go over the screen size, but the encoding process takes a fair amount of time, so I wouldn't code big images (I have been working on 500x500). If anyone has a way of speeding up the process, I would love to hear it.

Enjoy!

XdM
Attachments
Steganography.jpg
Last edited by Xero on Wed Oct 28, 2020 6:56 am, edited 1 time in total.

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 8169
Joined: Sat Apr 08, 2006 7:05 am
Location: Los Angeles
Contact:

Re: Steganography Coder

Post by FourthWorld » Mon Oct 26, 2020 5:01 am

If anyone has a way of speeding up the process, I would love to hear it.
If you can post the whole stack file so we can experiment with it I'll bet the folks here can speed it up a lot.
Richard Gaskin
Community volunteer LiveCode Community Liaison

LiveCode development, training, and consulting services: Fourth World Systems: http://FourthWorld.com
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

Xero
Posts: 77
Joined: Sat Jun 23, 2018 2:22 pm

Re: Steganography Coder

Post by Xero » Mon Oct 26, 2020 5:44 am

I tried to post the stack, but couldn't, even in a zipped file, due to size limitations. I don't have any space elsewhere to post it either sadly!
It will take a few minutes to reconstruct, but everything is in the post. All cut and paste, with a few numbers to change.
If anyone wants, I can send them the stack off-site.
XdM

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 8169
Joined: Sat Apr 08, 2006 7:05 am
Location: Los Angeles
Contact:

Re: Steganography Coder

Post by FourthWorld » Mon Oct 26, 2020 7:48 am

I'll bet you'd see at least a modest speed bump just changing "char" to "byte".
Richard Gaskin
Community volunteer LiveCode Community Liaison

LiveCode development, training, and consulting services: Fourth World Systems: http://FourthWorld.com
LiveCode User Group on Facebook : http://FaceBook.com/groups/LiveCodeUsers/

Xero
Posts: 77
Joined: Sat Jun 23, 2018 2:22 pm

Re: Steganography Coder

Post by Xero » Mon Oct 26, 2020 10:38 am

Thanks Richard.
Probably a mild increase in speed, but not noticeable. I will need to add a speed check to see what old vs new is. May be good for larger files, as every % decrease will make a big difference.
Thanks.
XdM

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 3445
Joined: Sun Jan 07, 2007 9:12 pm
Location: Bochum, Germany

Re: Steganography Coder

Post by bn » Tue Oct 27, 2020 5:28 pm

Hi Xero,

chartoNum and numToChar are both deprecated. Use numToByte and byteToNum instead. That will speed up the whole thing considerably, I think that is what Richard meant. And of course when addressing use byte instead of char. You are working on byte stuff, not chars.

For chars use numToNativeChar and nativeCharToNum.

Kind regards
Bernd

Post Reply

Return to “Multimedia”