Watermarking Algorithm 2
Watermarking Algorithm 2
Bitmap routines
support OS/2 1.x, Windows 3.x and OS/2 2.x single image bitmaps;
with 256 color images the basic idea of the algorithm is derived
from the program 'Spyder' by Lucas Natraj and Philip Tellis with
kind permission of the authors
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "hide4pgp.h"
#include "utils.h"
#include "scramble.h"
typedef union
{
UWORD16 RGB[3];
struct
{
UWORD16 L;
WORD16 a;
WORD16 b;
} Lab;
} PalEntry;
typedef struct
{
UBYTE Partner;
UBYTE Assigned; /* also a multi-purpose flag */
} PalRelated;
typedef struct
{
WORD32 Total; /* pixels with this color */
WORD32 HowOften[2]; /* 0: 0-bits, 1: 1-bits */
float Distance;
float LWeightedSum;
float aWeightedSum;
float bWeightedSum;
float Deposit;
} PalStatist;
int fillup = 1, smooth = TRUE; /* extern flags */
/*********************************************************************
RGB2Lab: converts palette entries in monitor RGB coordinates to
the CIE L*a*b* colorspace using the Microsoft/ Hewlett
Packard sRGB specification
parameter: pointer to palette entry
returns: nothing
*********************************************************************/
/*********************************************************************
mDeltaE: calculates the 'distance' (delta E) of two palette
entries in CIE L*a*b* coordinates with much more weight
on luminance (* 6.2), multiplied by a factor derived from
the difference in bit frequencies (differences around
the squareroot of the sums result in a factor of 1.1,
maximum factor 2.0)
parameter: two palette entries
returns: result
*********************************************************************/
d = sqrt (d);
f = (float) UseCount (x) * UseCount (y);
if (f > 0)
{
t = f > 1.0 ? log (49.0) / log (f) : 1.0;
f = (float) (ColorData[x].HowOften[0] - ColorData[x].HowOften[1]) *
(ColorData[y].HowOften[0] - ColorData[y].HowOften[1]) /
f;
f = (f > 0 ? pow (f, t) : - pow (-f, t)) / 3.0;
if (1.0 + f > (1.0 - f) * MAXDIST)
d = MAXDIST;
else
d *= (1.0 + f) / (1.0 - f);
}
return d;
}
/*********************************************************************
Mod2Pixel: injects the secret data bits into a block of 8 bit data
in memory
parameter: pointer to UBYTE array, size of array
returns: nothing
*********************************************************************/
/*********************************************************************
ModPixel: injects the length bits into a block of 8 bit data
in memory, switches to data routine after last byte
parameter: pointer to UBYTE array, size of array
returns: nothing
*********************************************************************/
while (count--)
{
if (NextBits (1) != ColorAttr[*block].Assigned)
*block = ColorAttr[*block].Partner;
block++;
/*********************************************************************
UpdateStat: registers bit to be stored with pixel contents, updates
color count and frequency
parameter: pixel value
returns: nothing
*********************************************************************/
ColorData[ColorIndex].Total++;
/*********************************************************************
GetBitFromPixel: get the bit assigned to the palette entry of the
next pixel of the disk file, or 0
parameter: Flag if bit should be returned
returns: requested bit as integer
*********************************************************************/
b = GetStegByte ();
/*********************************************************************
FillGaps: Distribute unused palette entries as partners for those
with distant partners on a longest distance first base
parameter: none
returns: nothing
*********************************************************************/
/*********************************************************************
AssignBits: Find out best bit values for all colors with partners;
let all colors vote for their preferred bit; every 1 to
be stored increases, every 0 decreases the vote for this
color; the vote of one color subtracts from the vote of
its partner; votes are corrected for standard deviation
parameter: none
returns: nothing
*********************************************************************/
/* vote and collect votes on the way, change sign every step */
q = i;
vote = 0.0;
do
{
vote = WeightedVote (q) - vote;
ColorAttr[q].Assigned = 2; /* mark: has voted */
q = ColorAttr[p = q].Partner; /* p: keep previous */
}
while (ColorAttr[q].Assigned < 2 && UseCount (q));
ColorAttr[i].Assigned = ColorAttr[q].Assigned ^ p;
}
}
}
/*********************************************************************
AdjustColors: Determine colors best suited for bit distribution with
least overall change, store assigned bit in RED[0]
parameter: none
returns: nothing
*********************************************************************/
if (smooth)
{
for (i = 0; i < ColorCount; i++)
{
if (UseCount (i))
{
p = ColorAttr[i].Partner;
Weight = ColorData[i].HowOften[! ColorAttr[i].Assigned];
ColorData[p].Deposit += Weight;
ColorData[p].LWeightedSum += Color[i].Lab.L * Weight;
ColorData[p].aWeightedSum += Color[i].Lab.a * Weight;
ColorData[p].bWeightedSum += Color[i].Lab.b * Weight;
}
}
}
Lab2RGB (&Color[i]);
/*********************************************************************
IsGreyScale: determines if palette is true greyscale, i.e.
Palette[i].Red = .Green = .Blue = i for all i
parameter: none; uses BMPInfoSize to determine palette offset
returns: result limited to UWORD16
*********************************************************************/
/* go to palette */
if (fseek (StegFile, 14 + BMPInfoSize, SEEK_SET))
UnexpEOF ();
/*********************************************************************
BMPNextBlock: positions the read pointer in StegFile to the beginning
of the next scanline
parameter: none; uses external variables:
position: offset of scanline from beginning of file
width: length of scanline in bytes
height: number of scanlines
LineOffset: offset to next scanline
returns: length of scanline in bytes or 0 if EOF
changed external variables:
position: points to next scanline
height: number of scanlines left
*********************************************************************/
/*********************************************************************
FirstLine: is called on the 1st nextblock call with 256 colore BMPs;
all initialization, palette and pixel analysis and
optimization is done here; chains then to and directs
all further calls to BMPNextBlock;
parameter: none; uses external variables:
flags: hiding, fillup, smooth
BMPInfoSize, position, width, height, LineOffset
MediaLength, StegLength
returns: the return code of BMPNextBlock
changed external variables:
ColorAttr[256] with bit values and partner colors
*********************************************************************/
UWORD32 FirstLine (void)
{
int i, j;
long ls, lc;
/* go to palette */
if (fseek (StegFile, 14 + BMPInfoSize, SEEK_SET))
UnexpEOF ();
if (hiding)
{
if (verbose > 1)
{
fputs ("\nAnalyzing palette ... ", stderr);
fflush (stderr);
}
RGB2Lab (&Color[i]);
if (verbose > 1)
{
fputs ("Done.\nAnalyzing picture ... ", stderr);
fflush (stderr);
}
if (verbose > 1)
{
fputs ("Done.\nFinding optimal encoding scheme ... ", stderr);
fflush (stderr);
}
if (verbose > 1)
{
fputs ("Done.\nOptimizing palette ... ", stderr);
fflush (stderr);
}
AdjustColors ();
free (ColorData);
free (Color);
if (verbose > 1)
{
fputs ("Done.\nEncoding data ... ", stderr);
fflush (stderr);
}
}
else
{
if (verbose > 1)
{
fputs ("\nGetting the encoding scheme ... ", stderr);
fflush (stderr);
}
if (verbose > 1)
{
fputs ("Done.\nDecoding data ... ", stderr);
fflush (stderr);
}
}
*NextLineM = BMPNextBlock;
return BMPNextBlock ();
}
/*********************************************************************
CheckIfBMP: checks and outputs the characteristics of StegFile;
aborts, if bitmap format not suited for steganography:
# of planes != 1, # of bits not 8 or 24, bitmap compressed
sets external variables for BMPNextBlock:
position: offset of first scanline from beginning of file
width: length of scanline in bytes
height: number of scanlines
LineOffset: offset to next scanline
for 8 bit color palettes when hiding:
1. with newer format and fewer than maximum # of colors
used or important these are reset to maximum for ste-
ganography almost certainly changes this number
2. the palette is optimized it switches are set
parameter: Location of Pointer to 'NextBlock'-Routine
read pointer set to second byte in file
returns: TRUE if recognized bitmap format
*********************************************************************/
if (verbose)
{
fputs ("Bitmap format <", stderr);
switch (BMPInfoSize)
{
case 12:
fputs ("OS/2 1", stderr);
break;
case 40:
fputs ("Windows 3", stderr);
break;
case 64:
fputs ("OS/2 2", stderr);
break;
}
fputs (".x> detected, ", stderr);
switch (BitCount)
{ case 1:
fputs ("monochrome (2", stderr);
break;
case 4:
fputs ("VGA (16", stderr);
break;
case 8:
fputs ("SVGA or greyscale (256", stderr);
break;
case 16:
fputs ("High Color (65536", stderr);
break;
case 24:
fputs ("True Color (16.7 Mio", stderr);
break;
default:
fprintf (stderr, "unknown color depth (? = 2 ^ %i", BitCount);
}
fputs (" colors).\n", stderr);
if (verbose > 1)
fprintf (stderr, "Bitmap size %li x %li pixels.\n", width, height);
}
if (verbose > 1)
{
fprintf (stderr, "Actual size of bitmap data: %li Bytes.\n",
GetStegLong ());
fprintf (stderr, "Bitmap resolution: %li dpi horizontal ",
(GetStegLong () * 127 + 2500) / 5000);
fprintf (stderr, "and %li dpi vertical.\n",
(GetStegLong () * 127 + 2500) / 5000);
}
}
if (height == 0 || width == 0)
ErrorExit (BMPNOPIXELS, "Error (BMP): No picture ?!?\n");
if (planes != 1)
ErrorExit (BMPNOT1PLANE,
"Error (BMP): Pixels divided into more than one plane.\n");
switch (BitCount)
{
case 24:
width *= 3;
case 8:
break;
default:
ErrorExit (BMPCOLORCNT,
"Error (BMP): Color depth not suited for steganography.\n");
}
DataSize = 1;
MediaLength = width * height;
LineOffset = width + 3 & ~3ul; /* adjust scanline to WORD32 boundary */
return TRUE;
}