www.gusucode.com > 基于dwt的视频数字水印源代码 > 基于dwt的视频数字水印源代码\code\HaYDWT\EmbedRecoverProcess.cpp

    //Download by http://www.NewXing.com
//---------------------------------------------------------------------------


#pragma hdrstop

#include "EmbedRecoverProcess.h"
#include "EmbedVideo.h"
#include "WatRecover.h"
#include "Main.h"
#include "Common.h"
#include "FileServices.h"
#include "dwt.h"
#include "MemUtil.h"
#include <math.h>

//---------------------------------------------------------------------------

#pragma package(smart_init)

/*
    Embed AVI Video

    Constraints:
                    1.  Doesn't support AVI files with audio

*/


int EmbedWatermarkAVI_Chasan(   HWND thisHwnd,
                                AnsiString inVideoFileName,
                                AnsiString outVideoFileName,
                                // watermark inputs
                                Graphics::TBitmap *pEWbmpImage,
                                stcImageType EWImageType,
	                            UCHAR **Gray_Image,
                                long Gray_Image_Width,
                                long Gray_Image_Height,
                                // Key Image inputs
                                Graphics::TBitmap *pEWKeybmpImage,
                                stcImageType EWKImageType,
        	                    UCHAR **Gray_Image_Key,
                                long Gray_Image_Key_Width,
                                long Gray_Image_Key_Height,
                                // embed inputs
                                double GainFactor, // gain factor needed at embedding time
                                int DWTLevels, // wavelet tansform level
                                int LevelEmbed )  // Which level watermark will be embedded
{

    long AviWidth, AviHeight, i, j, iImg, jImg, iImgKey, jImgKey, NumFrames, iFrm, is, js;
    long tmpW, tmpH, EWWidth, EWHeight, EWKeyWidth, EWKeyHeight;
    long EmbedHorW, EmbedHorH, EmbedVerW, EmbedVerH;
    long StartEmbedHorW, StartEmbedHorH, StartEmbedVerW, StartEmbedVerH;
	TAviUtil *inAvi, *outAvi;
	HAVI AviIn, AviOut;
    UCHAR **TempImage;
    char **TempImageKey;
    UCHAR **Red, **Green, **Blue;
	double **waveRed, **waveGreen, **waveBlue;
    BlockInfoStrc DWTSegmentAdrs[10];
    int szSeg;


    // ------------------------
    // CHECK 1
    // there must be two images
    if ((EWImageType == Img_NONE) || (EWKImageType == Img_NONE) || (EWImageType == EWKImageType))
    {
        MessageDlg("You must select a Watermark and Key image.", mtError, TMsgDlgButtons() << mbOK, 0);
        return false;
    }

    // -----------------------------------------------------------
    // CHECK 2
    // Key image sizes MUST be equal to video DWT level size.
	// Open the AVI file and set handle
	AviIn = RDOpenAviFile(inVideoFileName.c_str(), true);
	if (AviIn == NULL)
	{
		DispError("ERROR! Cannot open AVI file.");
		return S_FALSE;
	}
	inAvi = (TAviUtil*)AviIn;
    AviWidth = inAvi->iWidth;
    AviHeight = inAvi->iHeight;
    NumFrames = inAvi->numframes;
    if ( DWTLevels < LevelEmbed )
    {
        MessageDlg("DWT Levels cannot be smaller than Embed Levels.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
    }
    // Check the dimensions for compatability.
    if (AviWidth%(1 << DWTLevels) || AviHeight%(1 << DWTLevels))
	{
        AnsiString sss = "width and height of input video must be divisible by 2^" + IntToStr(DWTLevels) + " = " + IntToStr((int)pow(2, DWTLevels));
        MessageDlg(sss, mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
	}
    if ((DWTLevels == 2) || (DWTLevels == 3))
    {
        if (LevelEmbed == 1)
        {
            tmpW = AviWidth >> 1; // division by 2
            tmpH = AviHeight >> 1;
        }
        else if (LevelEmbed == 2)
        {
            tmpW = AviWidth >> 2; // division by 4
            tmpH = AviHeight >> 2;
        }
        else
        {
            MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
            RDCloseAvi(AviIn);
    	    inAvi = NULL;
            return false;
        }
    }
    else
    {
        MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
	    inAvi = NULL;
        return false;
    }
    // check the sizes
/*    if (EWKImageType == Img_PGMGray)
    {
        if ( (Gray_Image_Key_Width != tmpW) || (Gray_Image_Key_Height != tmpH))
        {
            MessageDlg("Size of Key must fit the DWT sub-frequency size of the video frame.\nCannot use this Key.", mtError, TMsgDlgButtons() << mbOK, 0);
            RdCloseAvi(AviIn);
	        inAvi = NULL;
            return false;
        }
    }
    else // BMP
    {
        if ( (pEWKeybmpImage->Width != tmpW) || (pEWKeybmpImage->Height != tmpH))
        {
            MessageDlg("Size of Key must fit the DWT sub-frequency size of the video frame.\nCannot use this Key.", mtError, TMsgDlgButtons() << mbOK, 0);
            RdCloseAvi(AviIn);
	        inAvi = NULL;
            return false;
        }
    }
    // -----------------------------------------------------------
*/
    // if we came this far, inAvi structure is still open.
    // We will continue to use this structure.
/*
    // we only support 8-bmp and PGM images
    if ( (EWImageType != Img_PGMGray) || (EWImageType != Img_BMPGray))
    {
            MessageDlg("Watermark must be 8-bit BMP or PGM.", mtError, TMsgDlgButtons() << mbOK, 0);
            RDCloseAvi(AviIn);
	        inAvi = NULL;
            return false;
    }
*/

    // Erase file if exists
    if (FileExists(outVideoFileName.c_str()))
        DeleteFileA(outVideoFileName.c_str());
    // Create Avi
	AviOut = CreateAvi(outVideoFileName.c_str(), inAvi->AviInfo.dwScale, inAvi->AviInfo.dwRate, NULL);
    if (MessageBox(thisHwnd, "Do you want to compress output AVI?","Question", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
    {
        HBITMAP hbm;
        HDC hdcscreen=GetDC(0), hdc=CreateCompatibleDC(hdcscreen);
        ReleaseDC(0,hdcscreen);
        BITMAPINFO bmi;
	    ZeroMemory(&bmi,sizeof(bmi));
	    BITMAPINFOHEADER &bih = bmi.bmiHeader;
	    bih.biSize = sizeof(bih);
	    bih.biWidth = AviWidth;
    	bih.biHeight = AviHeight;
	    bih.biPlanes = 1;
	    bih.biBitCount = 24;
	    bih.biCompression = BI_RGB;
	    bih.biSizeImage = ((bih.biWidth*bih.biBitCount/8+3)&0xFFFFFFFC)*bih.biHeight;
	    bih.biXPelsPerMeter = 1;
	    bih.biYPelsPerMeter = 1;
	    bih.biClrUsed = 0;
	    bih.biClrImportant = 0;

	    void *bits;

	    hbm = CreateDIBSection(hdc,(BITMAPINFO*)&bih,DIB_RGB_COLORS,&bits,NULL,NULL);

	    AVICOMPRESSOPTIONS opts;
	    ZeroMemory(&opts,sizeof(opts));
	    opts.fccHandler = mmioFOURCC('d','i','v','x');
	    SetAviVideoCompression(AviOut, hbm, &opts, true, thisHwnd);
	    DeleteDC(hdc);
	    DeleteObject(hbm);
    }

    /* Allocate required buffers */
    // watermark
    if (EWImageType == Img_PGMGray)
    {
        TempImage = Alloc_2D_UCHAR(Gray_Image_Width, Gray_Image_Height);
        EWWidth  = Gray_Image_Width;
        EWHeight = Gray_Image_Height;
    }
    else
    {
        TempImage = Alloc_2D_UCHAR(pEWbmpImage->Width, pEWbmpImage->Height);
        EWWidth  = pEWbmpImage->Width;
        EWHeight = pEWbmpImage->Height;
    }
    // key
    if  (EWKImageType == Img_PGMGray)
    {
        TempImageKey = Alloc_2D_char(Gray_Image_Key_Width, Gray_Image_Key_Height);
        EWKeyWidth  = Gray_Image_Key_Width;
        EWKeyHeight = Gray_Image_Key_Height;
    }
    else
    {
        TempImageKey = Alloc_2D_char(pEWKeybmpImage->Width, pEWKeybmpImage->Height);
        EWKeyWidth  = pEWKeybmpImage->Width;
        EWKeyHeight = pEWKeybmpImage->Height;
    }
    Red = Alloc_2D_UCHAR(AviWidth, AviHeight);
    Green = Alloc_2D_UCHAR(AviWidth, AviHeight);
	Blue = Alloc_2D_UCHAR(AviWidth, AviHeight);
	waveRed = Alloc_2D_Double(AviWidth, AviHeight);
	waveGreen = Alloc_2D_Double(AviWidth, AviHeight);
	waveBlue = Alloc_2D_Double(AviWidth, AviHeight);


	int wLL = AviWidth >> DWTLevels;
	int hLL = AviHeight >> DWTLevels;
	/*********************************/
	/* Determine Incriment positions */
	DWTSegmentAdrs[0].x   = 0;
	DWTSegmentAdrs[0].y   = 0;
	/****************/
	/*   Level=3    */
	DWTSegmentAdrs[1].x   = 0;
	DWTSegmentAdrs[1].y   = hLL;

   	DWTSegmentAdrs[2].x   = wLL;
    DWTSegmentAdrs[2].y   = 0;

  	DWTSegmentAdrs[3].x   = wLL;
    DWTSegmentAdrs[3].y   = hLL;
    szSeg = 4;
    if (DWTLevels > 1)
    {
    	/****************/
	    /*   Level=2    */
    	DWTSegmentAdrs[4].x   = 0;                  //  South - West
	    DWTSegmentAdrs[4].y   = hLL<<1; // hLL*2

       	DWTSegmentAdrs[5].x   = wLL<<1;            // North - East
        DWTSegmentAdrs[5].y   = 0;

    	DWTSegmentAdrs[6].x   = wLL<<1;            // Diagonal
	    DWTSegmentAdrs[6].y   = hLL<<1; // hLL * 2
        szSeg = 7;
        if (DWTLevels == 3)
        {
        	/****************/
	        /*   Level=1    */
	        DWTSegmentAdrs[7].x   = 0;
        	DWTSegmentAdrs[7].y   = hLL<<2;

    	    DWTSegmentAdrs[8].x   = wLL<<2;
    	    DWTSegmentAdrs[8].y   = 0;

        	DWTSegmentAdrs[9].x   = wLL<<2;
    	    DWTSegmentAdrs[9].y   = hLL<<2;
        	/*********************************/
            szSeg = 10;
        }
    }
    // Determine embed starting and ending positions.
    // Only supports embedding in Level 1 or 2 after DWT_Level_2 or DWT_Level_3 transform
    if ((DWTLevels == 2) && (LevelEmbed == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }
    else if ((DWTLevels == 2) && (LevelEmbed == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[1].x;
        StartEmbedHorH = DWTSegmentAdrs[1].y;
        StartEmbedVerW = DWTSegmentAdrs[2].x;
        StartEmbedVerH = DWTSegmentAdrs[2].y;
        EmbedHorW = StartEmbedHorW + wLL;
        EmbedHorH = StartEmbedHorH + hLL;
        EmbedVerW = StartEmbedVerW + wLL;
        EmbedVerH = StartEmbedVerH + hLL;
    }
    else if ((DWTLevels == 3) && (LevelEmbed == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[7].x;
        StartEmbedHorH = DWTSegmentAdrs[7].y;
        StartEmbedVerW = DWTSegmentAdrs[8].x;
        StartEmbedVerH = DWTSegmentAdrs[8].y;
        EmbedHorW = StartEmbedHorW + wLL * 4;
        EmbedHorH = StartEmbedHorH + hLL * 4;
        EmbedVerW = StartEmbedVerW + wLL * 4;
        EmbedVerH = StartEmbedVerH + hLL * 4;
    }
    else if ((DWTLevels == 3) && (LevelEmbed == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }

//    long AviWidth, AviHeight, i, j, iImg, jImg, iImgKey, jImgKey;
//    long tmpW, tmpH, EWWidth, EWHeight, EWKeyWidth, EWKeyHeight;
//    long EmbedHorW, EmbedHorH, EmbedVerW, EmbedVerH;
//    long StartEmbedHorW, StartEmbedHorH, StartEmbedVerW, StartEmbedVerH;


    // don't scale watermark image. we don't need.
    if (EWImageType == Img_PGMGray)
    {
        // scale watermark image to
        for(i=0; i < EWHeight; i++)
        {
            for(j=0; j < EWWidth; j++)
            {
                TempImage[i][j] =  Gray_Image[i][j];
            }
        }
    }
    else
    {
       // scale watermark image to
        for(i=0; i < EWHeight; i++)
        {
            for(j=0; j < EWWidth; j++)
            {
                TempImage[i][j] = (UCHAR)(pEWbmpImage->Canvas->Pixels[j][i] & 0xFF);
            }
        }
    }

    // Scale Key Image to 0-1
    if (EWKImageType == Img_PGMGray)
    {
        // scale watermark image to
        for(i=0; i < EWKeyHeight; i++)
        {
            for(j=0; j < EWKeyWidth; j++)
            {
                //TempImageKey[i][j] = (double)Gray_Image_Key[i][j] / 255.0;
                TempImageKey[i][j] = Gray_Image_Key[i][j];
            }
        }
    }
    else
    {
       // scale watermark image to
        for(i=0; i < EWKeyHeight; i++)
        {
            for(j=0; j < EWKeyWidth; j++)
            {
                //TempImageKey[i][j] = (double)( pEWKeybmpImage->Canvas->Pixels[j][i] & 0xFF )  / 255.0;
                TempImageKey[i][j] = (ULONG)pEWKeybmpImage->Canvas->Pixels[j][i] & 0xFF;
            }
        }
    }

    for(i=0; i < EWKeyHeight; i++)
    {
        for(j=0; j < EWKeyWidth; j++)
        {
            if (TempImageKey[i][j] == 0)
                TempImageKey[i][j] = -1;
            else
                TempImageKey[i][j] = 1;
        }
    }

    int tmpImGGG;
    int aaH, abH, baH, bbH, aaW, abW, baW, bbW;

    FormEmbedVideo->ButtonCancel->Enabled = true;
    FormEmbedVideo->ButtonEmbed->Enabled = false;
    FormEmbedVideo->LabelBlink->Visible = true;
    Application->ProcessMessages();
    for(iFrm=0; iFrm<NumFrames; iFrm++)  // for each frame
    {
        // Read Video
        RDGetFrame(AviIn, iFrm, Red, Green, Blue);
        // DWT Transform
    	waveletTransform2D(Red, waveRed, AviWidth, AviHeight, DWTLevels, true);
		waveletTransform2D(Green, waveGreen, AviWidth, AviHeight, DWTLevels, true);
		waveletTransform2D(Blue, waveBlue, AviWidth, AviHeight, DWTLevels, true);

        // EMBED the image
        for(i=0; i < EWHeight; i++)
        {
            for(j=0; j < EWWidth; j++)
            {
                tmpImGGG = TempImage[i][j];
                if (tmpImGGG > 0 )
                    tmpImGGG = -1;
                else
                    tmpImGGG = 1;
                is = i*2;
                js = j*2;

                // Horizontal scan positions
                aaH = StartEmbedHorH + is; // + 0
                abH = StartEmbedHorH + is + 1;
                aaW = StartEmbedHorW + js; // + 0
                abW = StartEmbedHorW + js + 1;
                // Vertical scan positions
                baH = StartEmbedVerH + is; // + 0
                bbH = StartEmbedVerH + is + 1;
                baW = StartEmbedVerW + js; // + 0
                bbW = StartEmbedVerW + js + 1;

                //  RED
                // Horizontal Scan
                waveRed[aaH][aaW] += GainFactor * TempImageKey[is][js] * tmpImGGG;
                waveRed[aaH][abW] += GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                waveRed[abH][aaW] += GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                waveRed[abH][abW] += GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;
                // Vertical Scan
                waveRed[baH][baW] += GainFactor * TempImageKey[is][js] * tmpImGGG;
                waveRed[baH][bbW] += GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                waveRed[bbH][baW] += GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                waveRed[bbH][bbW] += GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;

                // GREEN
                // Horizontal Scan
                waveGreen[aaH][aaW] += GainFactor * TempImageKey[is][js] * tmpImGGG;
                waveGreen[aaH][abW] += GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                waveGreen[abH][aaW] += GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                waveGreen[abH][abW] += GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;
                // Vertical Scan
                waveGreen[baH][baW] += GainFactor * TempImageKey[is][js] * tmpImGGG;
                waveGreen[baH][bbW] += GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                waveGreen[bbH][baW] += GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                waveGreen[bbH][bbW] += GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;

                // BLUE
                // Horizontal Scan
                waveBlue[aaH][aaW] += GainFactor * TempImageKey[is][js] * tmpImGGG;
                waveBlue[aaH][abW] += GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                waveBlue[abH][aaW] += GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                waveBlue[abH][abW] += GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;
                // Vertical Scan
                waveBlue[baH][baW] += GainFactor * TempImageKey[is][js] * tmpImGGG;
                waveBlue[baH][bbW] += GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                waveBlue[bbH][baW] += GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                waveBlue[bbH][bbW] += GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;

            } // j
        } // i

    	/*  Inverse wavelet transform.Back to Image matrix  */
		waveletTransform2D(Red, waveRed, AviWidth, AviHeight, DWTLevels, false);
		waveletTransform2D(Green, waveGreen, AviWidth, AviHeight, DWTLevels, false);
		waveletTransform2D(Blue, waveBlue, AviWidth, AviHeight, DWTLevels, false);

        /* WRITE Image to NEW AVI File */
		AddAviFrameMatrix(AviOut, AviWidth, AviHeight, Red, Green, Blue);

        FormEmbedVideo->LabelEmbed->Caption = IntToStr(iFrm+1) + "/" + IntToStr(NumFrames) + " frames";
        FormEmbedVideo->BarEmbed->Percent = ((iFrm+1)*100)/NumFrames;
        Application->ProcessMessages();
        if (FormEmbedVideo->CancelEmbed)
        {
            FormEmbedVideo->ButtonCancel->Enabled = false;
            FormEmbedVideo->ButtonEmbed->Enabled = true;
            FormEmbedVideo->LabelBlink->Visible = false;

            StreamCopyAudio(AviIn, AviOut);

            // Close and Free all handles
            Free_2D_UCHAR(TempImage, EWHeight);
            Free_2D_char(TempImageKey, EWKeyHeight);
            Free_2D_UCHAR(Red, AviHeight);
            Free_2D_UCHAR(Green, AviHeight);
		    Free_2D_UCHAR(Blue, AviHeight);
        	Free_2D_Double(waveRed, AviHeight);
		    Free_2D_Double(waveGreen, AviHeight);
        	Free_2D_Double(waveBlue, AviHeight);
            RDCloseAvi(AviIn);
            CloseAvi(AviOut);
		    inAvi = NULL;
            outAvi = NULL;
            return true;
        }
        Application->ProcessMessages();
    }
    FormEmbedVideo->ButtonEmbed->Enabled = true;
    FormEmbedVideo->ButtonCancel->Enabled = false;
    FormEmbedVideo->LabelBlink->Visible = false;

    StreamCopyAudio(AviIn, AviOut);

    // Close and Free all handles
    Free_2D_UCHAR(TempImage, EWHeight);
    Free_2D_char(TempImageKey, EWKeyHeight);
    Free_2D_UCHAR(Red, AviHeight);
    Free_2D_UCHAR(Green, AviHeight);
	Free_2D_UCHAR(Blue, AviHeight);
	Free_2D_Double(waveRed, AviHeight);
	Free_2D_Double(waveGreen, AviHeight);
	Free_2D_Double(waveBlue, AviHeight);
    RDCloseAvi(AviIn);
    CloseAvi(AviOut);
	inAvi = NULL;
    outAvi = NULL;
    return true;
}

/*
    Detect AVI Video
*/
int RecoverWatermarkAVI_Chasan( HWND thisHwnd,
                                AnsiString inVideoFileName,
                                // watermark inputs
                                Graphics::TBitmap *pRWbmpImage,
                                stcImageType RWImageType,
	                            UCHAR **Gray_Image_RW,
                                long Gray_Image_RW_Width,
                                long Gray_Image_RW_Height,
                                // Key Image inputs
                                Graphics::TBitmap *pRWKeybmpImage,
                                stcImageType RWKImageType,
        	                    UCHAR **Gray_Image_RW_Key,
                                long Gray_Image_RW_Key_Width,
                                long Gray_Image_RW_Key_Height,
                                // embed inputs
                                double BetaFactorRW,
                                int DWTLevelsRW, // wavelet tansform level
                                int LevelEmbedRW,  // in which level watermark has been embedded
                                bool AllFrames,
                                long FrameNo)
{

    long AviWidth, AviHeight, i, j, iImg, jImg, iImgKey, jImgKey, NumFrames, iFrm, is, js;
    long tmpW, tmpH, RWWidth, RWHeight, RWKeyWidth, RWKeyHeight;
    long EmbedHorW, EmbedHorH, EmbedVerW, EmbedVerH;
    long StartEmbedHorW, StartEmbedHorH, StartEmbedVerW, StartEmbedVerH;
	TAviUtil *inAvi;
	HAVI AviIn;
    UCHAR **TempImage;
    char **TempImageKey;
    UCHAR **Red, **Green, **Blue;
	double **waveRed, **waveGreen, **waveBlue;
    BlockInfoStrc DWTSegmentAdrs[10];
    int szSeg;
    Graphics::TBitmap *pBitmapTMP;

    int tmpImGGG;
    int aaH, abH, baH, bbH, aaW, abW, baW, bbW;
    double mA1, mA2, mcorr, corr, corr1, corr2, A11, A12, A13, A14, A21, A22, A23, A24, mB, B, B1, B2, B3, B4;
    double tmp, tmp1, tmp2, tmp3, fThreshold, mCorrRed, mCorrGreen, mCorrBlue, WatCorr, WatBER;
    double  *tmpCorrM;
    TListItem *ListItem;
    AnsiString strTmp;

    // ------------------------
    // CHECK 1
    // there must be two images
    if ((RWKImageType == Img_NONE))
    {
        MessageDlg("You must select a Watermark and Key image.", mtError, TMsgDlgButtons() << mbOK, 0);
        return false;
    }

    // -----------------------------------------------------------
    // CHECK 2
    // Key image sizes MUST be equal to video DWT level size.
	// Open the AVI file and set handle
	AviIn = RDOpenAviFile(inVideoFileName.c_str(), true);
	if (AviIn == NULL)
	{
		DispError("ERROR! Cannot open AVI file.");
		return S_FALSE;
	}
	inAvi = (TAviUtil*)AviIn;
    AviWidth = inAvi->iWidth;
    AviHeight = inAvi->iHeight;
    NumFrames = inAvi->numframes;
    if ( DWTLevelsRW < LevelEmbedRW )
    {
        MessageDlg("DWT Levels cannot be smaller than Embed Levels.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
    }
    // Check the dimensions for compatability.
    if (AviWidth%(1 << DWTLevelsRW) || AviHeight%(1 << DWTLevelsRW))
	{
        AnsiString sss = "width and height of input video must be divisible by 2^" + IntToStr(DWTLevelsRW) + " = " + IntToStr((int)pow(2, DWTLevelsRW));
        MessageDlg(sss, mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
	}
    if ((DWTLevelsRW == 2) || (DWTLevelsRW == 3))
    {
        if (LevelEmbedRW == 1)
        {
            tmpW = AviWidth >> 1; // division by 2
            tmpH = AviHeight >> 1;
        }
        else if (LevelEmbedRW == 2)
        {
            tmpW = AviWidth >> 2; // division by 4
            tmpH = AviHeight >> 2;
        }
        else
        {
            MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
            RDCloseAvi(AviIn);
    	    inAvi = NULL;
            return false;
        }
    }
    else
    {
        MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
	    inAvi = NULL;
        return false;
    }
    // if we came this far, inAvi structure is still open.
    // We will continue to use this structure.


    /* Allocate required buffers */
    // original watermark
    if (RWImageType == Img_PGMGray)
    {
        TempImage = Alloc_2D_UCHAR(Gray_Image_RW_Width, Gray_Image_RW_Height);
        RWWidth  = Gray_Image_RW_Width;
        RWHeight = Gray_Image_RW_Height;
    }
    else if (RWImageType != Img_NONE)
    {
        TempImage = Alloc_2D_UCHAR(pRWbmpImage->Width, pRWbmpImage->Height);
        RWWidth  = pRWbmpImage->Width;
        RWHeight = pRWbmpImage->Height;
    }
    // key
    if  (RWKImageType == Img_PGMGray)
    {
        TempImageKey = Alloc_2D_char(Gray_Image_RW_Key_Width, Gray_Image_RW_Key_Height);
        RWKeyWidth  = Gray_Image_RW_Key_Width;
        RWKeyHeight = Gray_Image_RW_Key_Height;
    }
    else
    {
        TempImageKey = Alloc_2D_char(pRWKeybmpImage->Width, pRWKeybmpImage->Height);
        RWKeyWidth  = pRWKeybmpImage->Width;
        RWKeyHeight = pRWKeybmpImage->Height;
    }

    Red = Alloc_2D_UCHAR(AviWidth, AviHeight);
    Green = Alloc_2D_UCHAR(AviWidth, AviHeight);
	Blue = Alloc_2D_UCHAR(AviWidth, AviHeight);
	waveRed = Alloc_2D_Double(AviWidth, AviHeight);
	waveGreen = Alloc_2D_Double(AviWidth, AviHeight);
	waveBlue = Alloc_2D_Double(AviWidth, AviHeight);


	int wLL = AviWidth >> DWTLevelsRW;
	int hLL = AviHeight >> DWTLevelsRW;
	/*********************************/
	/* Determine Incriment positions */
	DWTSegmentAdrs[0].x   = 0;
	DWTSegmentAdrs[0].y   = 0;
	/****************/
	/*   Level=3    */
	DWTSegmentAdrs[1].x   = 0;
	DWTSegmentAdrs[1].y   = hLL;

   	DWTSegmentAdrs[2].x   = wLL;
    DWTSegmentAdrs[2].y   = 0;

  	DWTSegmentAdrs[3].x   = wLL;
    DWTSegmentAdrs[3].y   = hLL;
    szSeg = 4;
    if (DWTLevelsRW > 1)
    {
    	/****************/
	    /*   Level=2    */
    	DWTSegmentAdrs[4].x   = 0;                  //  South - West
	    DWTSegmentAdrs[4].y   = hLL<<1; // hLL*2

       	DWTSegmentAdrs[5].x   = wLL<<1;            // North - East
        DWTSegmentAdrs[5].y   = 0;

    	DWTSegmentAdrs[6].x   = wLL<<1;            // Diagonal
	    DWTSegmentAdrs[6].y   = hLL<<1; // hLL * 2
        szSeg = 7;
        if (DWTLevelsRW == 3)
        {
        	/****************/
	        /*   Level=1    */
	        DWTSegmentAdrs[7].x   = 0;
        	DWTSegmentAdrs[7].y   = hLL<<2;

    	    DWTSegmentAdrs[8].x   = wLL<<2;
    	    DWTSegmentAdrs[8].y   = 0;

        	DWTSegmentAdrs[9].x   = wLL<<2;
    	    DWTSegmentAdrs[9].y   = hLL<<2;
        	/*********************************/
            szSeg = 10;
        }
    }
    // Determine embed starting and ending positions.
    // Only supports embedding in Level 1 or 2 after DWT_Level_2 or DWT_Level_3 transform
    if ((DWTLevelsRW == 2) && (LevelEmbedRW == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }
    else if ((DWTLevelsRW == 2) && (LevelEmbedRW == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[1].x;
        StartEmbedHorH = DWTSegmentAdrs[1].y;
        StartEmbedVerW = DWTSegmentAdrs[2].x;
        StartEmbedVerH = DWTSegmentAdrs[2].y;
        EmbedHorW = StartEmbedHorW + wLL;
        EmbedHorH = StartEmbedHorH + hLL;
        EmbedVerW = StartEmbedVerW + wLL;
        EmbedVerH = StartEmbedVerH + hLL;
    }
    else if ((DWTLevelsRW == 3) && (LevelEmbedRW == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[7].x;
        StartEmbedHorH = DWTSegmentAdrs[7].y;
        StartEmbedVerW = DWTSegmentAdrs[8].x;
        StartEmbedVerH = DWTSegmentAdrs[8].y;
        EmbedHorW = StartEmbedHorW + wLL * 4;
        EmbedHorH = StartEmbedHorH + hLL * 4;
        EmbedVerW = StartEmbedVerW + wLL * 4;
        EmbedVerH = StartEmbedVerH + hLL * 4;
    }
    else if ((DWTLevelsRW == 3) && (LevelEmbedRW == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }

    // fill temp watermark image matrix
    if (RWImageType == Img_PGMGray)
    {
        for(i=0; i < RWHeight; i++)
        {
            for(j=0; j < RWWidth; j++)
            {
                TempImage[i][j] =  Gray_Image_RW[i][j];
            }
        }
    }
    else if (RWImageType != Img_NONE)
    {
        for(i=0; i < RWHeight; i++)
        {
            for(j=0; j < RWWidth; j++)
            {
                TempImage[i][j] = (UCHAR)(pRWbmpImage->Canvas->Pixels[j][i] & 0xFF);
            }
        }
    }
    // fill temp key image matrix
    if (RWKImageType == Img_PGMGray)
    {
        for(i=0; i < RWKeyHeight; i++)
        {
            for(j=0; j < RWKeyWidth; j++)
            {
                TempImageKey[i][j] = Gray_Image_RW_Key[i][j];
            }
        }
    }
    else
    {
        for(i=0; i < RWKeyHeight; i++)
        {
            for(j=0; j < RWKeyWidth; j++)
            {
                TempImageKey[i][j] = (ULONG)pRWKeybmpImage->Canvas->Pixels[j][i] & 0xFF;
            }
        }
    }

    for(i=0; i < RWKeyHeight; i++)
    {
        for(j=0; j < RWKeyWidth; j++)
        {
            if (TempImageKey[i][j] == 0)
                TempImageKey[i][j] = -1;
            else
                TempImageKey[i][j] = 1;
        }
    }


    // temporary recover image
    pBitmapTMP = new Graphics::TBitmap();
    pBitmapTMP->PixelFormat = pf24bit;
    pBitmapTMP->Width = RWKeyWidth/2;
    pBitmapTMP->Height = RWKeyHeight/2;
    TCanvas *tmpCanvas = pBitmapTMP->Canvas;

    if (!AllFrames)   // only one frame
    {
        FormRecover->ButtonCancel->Enabled = true;
        FormRecover->ButtonRecover->Enabled = false;
    }
    else
    {
        FormRecover->ButtonCancel->Enabled = true;
        FormRecover->ButtonCalcBER->Enabled = false;
    }
    FormRecover->GroupBoxOptions->Enabled = false;
    FormRecover->ListResult->Items->Clear();
    Application->ProcessMessages();

    tmpCorrM = (double *)malloc((RWKeyWidth/2) * (RWKeyHeight/2) * sizeof(double));

    for(iFrm=0; iFrm<NumFrames; iFrm++)  // for each frame
    {
        // Read Video
        if (!AllFrames)  // only one frame
        {
            RDGetFrame(AviIn, FrameNo, Red, Green, Blue);
        }
        else  // all frames
        {
            RDGetFrame(AviIn, iFrm, Red, Green, Blue);
        }

        // DWT Transform
    	waveletTransform2D(Red, waveRed, AviWidth, AviHeight, DWTLevelsRW, true);
		waveletTransform2D(Green, waveGreen, AviWidth, AviHeight, DWTLevelsRW, true);
		waveletTransform2D(Blue, waveBlue, AviWidth, AviHeight, DWTLevelsRW, true);

        corr = 0.0;
        for(i=0; i < RWKeyHeight/2; i++)
        {
            for(j=0; j < RWKeyWidth/2; j++)
            {
                is = i*2;
                js = j*2;
                // Horizontal scan positions
                aaH = StartEmbedHorH + is; // + 0
                abH = StartEmbedHorH + is + 1;
                aaW = StartEmbedHorW + js; // + 0
                abW = StartEmbedHorW + js + 1;
                // Vertical scan positions
                baH = StartEmbedVerH + is; // + 0
                bbH = StartEmbedVerH + is + 1;
                baW = StartEmbedVerW + js; // + 0
                bbW = StartEmbedVerW + js + 1;

                //  RED
                // image matrix elements
                A11 = waveRed[aaH][aaW];
                A12 = waveRed[aaH][abW];
                A13 = waveRed[abH][aaW];
                A14 = waveRed[abH][abW];
                mA1 = (A11+A12+A13+A14)/4.0;

                A21 = waveRed[baH][baW];
                A22 = waveRed[baH][bbW];
                A23 = waveRed[bbH][baW];
                A24 = waveRed[bbH][bbW];
                mA2 = (A21+A22+A23+A24)/4.0;

                B1 = TempImageKey[is][js];
                B2 = TempImageKey[is][js+1];
                B3 = TempImageKey[is+1][js];
                B4 = TempImageKey[is+1][js+1];
                mB = (B1 + B2 + B3 + B4)/ 4.0;

                // Horizontal Scan
                tmp1 = (A11-mA1)*(B1 - mB) + (A12-mA1)*(B2 - mB) + (A13-mA1)*(B3 - mB) + (A14-mA1)*(B4 - mB);
                tmp2 = (A11-mA1)*(A11-mA1) + (A12-mA1)*(A12-mA1) + (A13-mA1)*(A13-mA1) + (A14-mA1)*(A14-mA1);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr1 = 0;
                else
                    corr1 = (tmp1)/(sqrt(tmp2*tmp3));

                // Vertical Scan
                tmp1 = (A21-mA2)*(B1 - mB) + (A22-mA2)*(B2 - mB) + (A23-mA2)*(B3 - mB) + (A24-mA2)*(B4 - mB);
                tmp2 = (A21-mA2)*(A21-mA2) + (A22-mA2)*(A22-mA2) + (A23-mA2)*(A23-mA2) + (A24-mA2)*(A24-mA2);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr2 = 0;
                else
                    corr2 = (tmp1)/(sqrt(tmp2*tmp3));

                mCorrRed = (corr1 + corr2)/2.0;

                //  GREEN
                // image matrix elements
                A11 = waveGreen[aaH][aaW];
                A12 = waveGreen[aaH][abW];
                A13 = waveGreen[abH][aaW];
                A14 = waveGreen[abH][abW];
                mA1 = (A11+A12+A13+A14)/4.0;

                A21 = waveGreen[baH][baW];
                A22 = waveGreen[baH][bbW];
                A23 = waveGreen[bbH][baW];
                A24 = waveGreen[bbH][bbW];
                mA2 = (A21+A22+A23+A24)/4.0;

                B1 = TempImageKey[is][js];
                B2 = TempImageKey[is][js+1];
                B3 = TempImageKey[is+1][js];
                B4 = TempImageKey[is+1][js+1];
                mB = (B1 + B2 + B3 + B4)/ 4.0;

                // Horizontal Scan
                tmp1 = (A11-mA1)*(B1 - mB) + (A12-mA1)*(B2 - mB) + (A13-mA1)*(B3 - mB) + (A14-mA1)*(B4 - mB);
                tmp2 = (A11-mA1)*(A11-mA1) + (A12-mA1)*(A12-mA1) + (A13-mA1)*(A13-mA1) + (A14-mA1)*(A14-mA1);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr1 = 0;
                else
                    corr1 = (tmp1)/(sqrt(tmp2*tmp3));
                // Vertical Scan
                tmp1 = (A21-mA2)*(B1 - mB) + (A22-mA2)*(B2 - mB) + (A23-mA2)*(B3 - mB) + (A24-mA2)*(B4 - mB);
                tmp2 = (A21-mA2)*(A21-mA2) + (A22-mA2)*(A22-mA2) + (A23-mA2)*(A23-mA2) + (A24-mA2)*(A24-mA2);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr2 = 0;
                else
                    corr2 = (tmp1)/(sqrt(tmp2*tmp3));

                mCorrGreen = (corr1 + corr2)/2.0;


                //  BLUE
                // image matrix elements
                A11 = waveBlue[aaH][aaW];
                A12 = waveBlue[aaH][abW];
                A13 = waveBlue[abH][aaW];
                A14 = waveBlue[abH][abW];
                mA1 = (A11+A12+A13+A14)/4.0;

                A21 = waveBlue[baH][baW];
                A22 = waveBlue[baH][bbW];
                A23 = waveBlue[bbH][baW];
                A24 = waveBlue[bbH][bbW];
                mA2 = (A21+A22+A23+A24)/4.0;

                B1 = TempImageKey[is][js];
                B2 = TempImageKey[is][js+1];
                B3 = TempImageKey[is+1][js];
                B4 = TempImageKey[is+1][js+1];
                mB = (B1 + B2 + B3 + B4)/ 4.0;

                // Horizontal Scan
                tmp1 = (A11-mA1)*(B1 - mB) + (A12-mA1)*(B2 - mB) + (A13-mA1)*(B3 - mB) + (A14-mA1)*(B4 - mB);
                tmp2 = (A11-mA1)*(A11-mA1) + (A12-mA1)*(A12-mA1) + (A13-mA1)*(A13-mA1) + (A14-mA1)*(A14-mA1);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr1 = 0;
                else
                    corr1 = (tmp1)/(sqrt(tmp2*tmp3));
                // Vertical Scan
                tmp1 = (A21-mA2)*(B1 - mB) + (A22-mA2)*(B2 - mB) + (A23-mA2)*(B3 - mB) + (A24-mA2)*(B4 - mB);
                tmp2 = (A21-mA2)*(A21-mA2) + (A22-mA2)*(A22-mA2) + (A23-mA2)*(A23-mA2) + (A24-mA2)*(A24-mA2);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr2 = 0;
                else
                    corr2 = (tmp1)/(sqrt(tmp2*tmp3));

                mCorrBlue = (corr1 + corr2)/2.0;

                // find the biggest correlation
                mcorr = mCorrRed;
                if (mCorrGreen > mcorr)
                    mcorr = mCorrGreen;
                if (mCorrBlue > mcorr)
                    mcorr = mCorrBlue;
                // add current correlation to total correlation
                tmpCorrM[i * RWKeyWidth/2 + j] = mcorr;
            } // j
        } // i

        // find mean correlation
        corr = 0.0;
        for(i=0; i < (RWKeyWidth * RWKeyHeight)/4; i++)
        {
            corr+= tmpCorrM[i];
        }
        mcorr = corr / (double)((RWKeyWidth * RWKeyHeight)/4.0);
        // find final theshold
        fThreshold = BetaFactorRW * mcorr;

        if (!AllFrames) // only one frame
        {
            // display image
            for(i=0; i < RWKeyHeight/2; i++)
            {
                for(j=0; j < RWKeyWidth/2; j++)
                {
                    if ( tmpCorrM[i * RWKeyWidth/2 + j] >= fThreshold)
                        tmpCanvas->Pixels[j][i] = (TColor)RGB(0, 0, 0);
                    else
                        tmpCanvas->Pixels[j][i] =   (TColor)RGB(255, 255, 255);
                }
            }
            FormRecover->ImageRecover->Picture->Assign(pBitmapTMP);
            FormRecover->ButtonSaveImage->Enabled = true;
            FormRecover->ButtonCancel->Enabled = false;
            FormRecover->ButtonRecover->Enabled = true;
            FormRecover->GroupBoxOptions->Enabled = true;

            // display BitErrors (BER)
            if (FormMain->RWImageType != Img_NONE)
            {
                // find BER and means
                WatBER = 0;
                for(i=0; i < RWHeight; i++)
                {
                    for(j=0; j < RWWidth; j++)
                    {
                        if ( (tmpCanvas->Pixels[j][i] & 0xFF ) != TempImage[i][j])
                           WatBER += 1;
                    }
                }
                WatBER = (WatBER * 100)/ (RWWidth * RWHeight);

                ListItem =  FormRecover->ListResult->Items->Add();
                // FrameNo
                ListItem->Caption = IntToStr(FrameNo);
                // BER
                strTmp.sprintf("%3.2f", WatBER);
                ListItem->SubItems->Add( strTmp );
            }
            delete pBitmapTMP;
            if (FormMain->wantRWCalcCorrelation)
                Free_2D_UCHAR(TempImage, RWHeight);  // watermark
            Free_2D_char(TempImageKey, RWKeyHeight);
            Free_2D_UCHAR(Red, AviHeight);
            Free_2D_UCHAR(Green, AviHeight);
		    Free_2D_UCHAR(Blue, AviHeight);
        	Free_2D_Double(waveRed, AviHeight);
		    Free_2D_Double(waveGreen, AviHeight);
        	Free_2D_Double(waveBlue, AviHeight);
            free(tmpCorrM);
            RDCloseAvi(AviIn);
		    inAvi = NULL;
            return true;
        }
        else   // all frames
        {
            FormRecover->LabelRecover->Caption = IntToStr(iFrm+1) + "/" + IntToStr(NumFrames) + " frames";
            FormRecover->BarRecover->Percent = ((iFrm+1)*100)/NumFrames;
            // add MER line into List
            // display BitErrors (BER)
            if (FormMain->RWImageType != Img_NONE)
            {
                // find BER and means
                WatBER = 0;
                for(i=0; i < RWHeight; i++)
                {
                    for(j=0; j < RWWidth; j++)
                    {
                        if ( tmpCorrM[i * RWKeyWidth/2 + j] >= fThreshold)
                            szSeg = 0;
                        else
                            szSeg = 255;
                        if ( szSeg != TempImage[i][j])
                           WatBER += 1;
                    }
                }
                WatBER = (WatBER * 100)/ (RWWidth * RWHeight);

                ListItem =  FormRecover->ListResult->Items->Add();
                // FrameNo
                ListItem->Caption = IntToStr(iFrm);
                // BER
                strTmp.sprintf("%3.2f", WatBER);
                FormRecover->recAllFrames = FormRecover->recAllFrames + strTmp + ", ";
                ListItem->SubItems->Add( strTmp );
            }
        }

        Application->ProcessMessages();
        if (FormRecover->cancelRecover)
        {
            if (!AllFrames)   // only one frame
            {
                FormRecover->ButtonCancel->Enabled = false;
                FormRecover->ButtonRecover->Enabled = true;
            }
            else
            {
                FormRecover->ButtonCancel->Enabled = false;
                FormRecover->ButtonCalcBER->Enabled = true;
            }
            FormRecover->GroupBoxOptions->Enabled = true;

            // Close and Free all handles
            if (FormMain->wantRWCalcCorrelation)
                Free_2D_UCHAR(TempImage, RWHeight);  // watermark
            Free_2D_char(TempImageKey, RWKeyHeight);
            Free_2D_UCHAR(Red, AviHeight);
            Free_2D_UCHAR(Green, AviHeight);
		    Free_2D_UCHAR(Blue, AviHeight);
        	Free_2D_Double(waveRed, AviHeight);
		    Free_2D_Double(waveGreen, AviHeight);
        	Free_2D_Double(waveBlue, AviHeight);
            free(tmpCorrM);
            RDCloseAvi(AviIn);
		    inAvi = NULL;
            return true;
        }
        Application->ProcessMessages();
    }
    if (!AllFrames) // only one frame
    {
        FormRecover->ButtonCancel->Enabled = false;
        FormRecover->ButtonRecover->Enabled = true;
    }
    else
    {
        FormRecover->ButtonCancel->Enabled = false;
        FormRecover->ButtonCalcBER->Enabled = true;
        FormRecover->ButtonSaveResults->Enabled = true;
    }
    FormRecover->GroupBoxOptions->Enabled = true;
    // Close and Free all handles
    if (FormMain->wantRWCalcCorrelation)
        Free_2D_UCHAR(TempImage, RWHeight); // watermark
    Free_2D_char(TempImageKey, RWKeyHeight);
    Free_2D_UCHAR(Red, AviHeight);
    Free_2D_UCHAR(Green, AviHeight);
	Free_2D_UCHAR(Blue, AviHeight);
	Free_2D_Double(waveRed, AviHeight);
	Free_2D_Double(waveGreen, AviHeight);
	Free_2D_Double(waveBlue, AviHeight);
    free(tmpCorrM);
    RDCloseAvi(AviIn);
	inAvi = NULL;
    return true;
}


int EmbedWatermarkAVI_Lifting(   HWND thisHwnd,
                                AnsiString inVideoFileName,
                                AnsiString outVideoFileName,
                                // watermark inputs
                                Graphics::TBitmap *pEWbmpImage,
                                stcImageType EWImageType,
	                            UCHAR **Gray_Image,
                                long Gray_Image_Width,
                                long Gray_Image_Height,
                                // Key Image inputs
                                Graphics::TBitmap *pEWKeybmpImage,
                                stcImageType EWKImageType,
        	                    UCHAR **Gray_Image_Key,
                                long Gray_Image_Key_Width,
                                long Gray_Image_Key_Height,
                                // embed inputs
                                double GainFactor, // gain factor needed at embedding time
                                int DWTLevels, // wavelet tansform level
                                int LevelEmbed )  // Which level watermark will be embedded
{

    long AviWidth, AviHeight, i, j, iImg, jImg, iImgKey, jImgKey, NumFrames, iFrm, is, js;
    long tmpW, tmpH, EWWidth, EWHeight, EWKeyWidth, EWKeyHeight;
    long EmbedHorW, EmbedHorH, EmbedVerW, EmbedVerH;
    long StartEmbedHorW, StartEmbedHorH, StartEmbedVerW, StartEmbedVerH;
	TAviUtil *inAvi, *outAvi;
	HAVI AviIn, AviOut;
    UCHAR **TempImage;
    char **TempImageKey;
    int **Red, **Green, **Blue;
    BlockInfoStrc DWTSegmentAdrs[10];
    int szSeg;


    // ------------------------
    // CHECK 1
    // there must be two images
    if ((EWImageType == Img_NONE) || (EWKImageType == Img_NONE) || (EWImageType == EWKImageType))
    {
        MessageDlg("You must select a Watermark and Key image.", mtError, TMsgDlgButtons() << mbOK, 0);
        return false;
    }

    // -----------------------------------------------------------
    // CHECK 2
    // Key image sizes MUST be equal to video DWT level size.
	// Open the AVI file and set handle
	AviIn = RDOpenAviFile(inVideoFileName.c_str(), true);
	if (AviIn == NULL)
	{
		DispError("ERROR! Cannot open AVI file.");
		return S_FALSE;
	}
	inAvi = (TAviUtil*)AviIn;
    AviWidth = inAvi->iWidth;
    AviHeight = inAvi->iHeight;
    NumFrames = inAvi->numframes;
    if ( DWTLevels < LevelEmbed )
    {
        MessageDlg("DWT Levels cannot be smaller than Embed Levels.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
    }
    // Check the dimensions for compatability.
    if (AviWidth%(1 << DWTLevels) || AviHeight%(1 << DWTLevels))
	{
        AnsiString sss = "width and height of input video must be divisible by 2^" + IntToStr(DWTLevels) + " = " + IntToStr((int)pow(2, DWTLevels));
        MessageDlg(sss, mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
	}
    if ((DWTLevels == 2) || (DWTLevels == 3))
    {
        if (LevelEmbed == 1)
        {
            tmpW = AviWidth >> 1; // division by 2
            tmpH = AviHeight >> 1;
        }
        else if (LevelEmbed == 2)
        {
            tmpW = AviWidth >> 2; // division by 4
            tmpH = AviHeight >> 2;
        }
        else
        {
            MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
            RDCloseAvi(AviIn);
    	    inAvi = NULL;
            return false;
        }
    }
    else
    {
        MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
	    inAvi = NULL;
        return false;
    }

    // Erase file if exists
    if (FileExists(outVideoFileName.c_str()))
        DeleteFileA(outVideoFileName.c_str());
    // Create Avi
	AviOut = CreateAvi(outVideoFileName.c_str(), inAvi->AviInfo.dwScale, inAvi->AviInfo.dwRate, NULL);
    if (MessageBox(thisHwnd, "Do you want to compress output AVI?","Question", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2) == IDYES)
    {
        HBITMAP hbm;
        HDC hdcscreen=GetDC(0), hdc=CreateCompatibleDC(hdcscreen);
        ReleaseDC(0,hdcscreen);
        BITMAPINFO bmi;
	    ZeroMemory(&bmi,sizeof(bmi));
	    BITMAPINFOHEADER &bih = bmi.bmiHeader;
	    bih.biSize = sizeof(bih);
	    bih.biWidth = AviWidth;
    	bih.biHeight = AviHeight;
	    bih.biPlanes = 1;
	    bih.biBitCount = 24;
	    bih.biCompression = BI_RGB;
	    bih.biSizeImage = ((bih.biWidth*bih.biBitCount/8+3)&0xFFFFFFFC)*bih.biHeight;
	    bih.biXPelsPerMeter = 1;
	    bih.biYPelsPerMeter = 1;
	    bih.biClrUsed = 0;
	    bih.biClrImportant = 0;

	    void *bits;

	    hbm = CreateDIBSection(hdc,(BITMAPINFO*)&bih,DIB_RGB_COLORS,&bits,NULL,NULL);

	    AVICOMPRESSOPTIONS opts;
	    ZeroMemory(&opts,sizeof(opts));
	    opts.fccHandler = mmioFOURCC('d','i','v','x');
	    SetAviVideoCompression(AviOut, hbm, &opts, true, thisHwnd);
	    DeleteDC(hdc);
	    DeleteObject(hbm);
    }

    /* Allocate required buffers */
    // watermark
    if (EWImageType == Img_PGMGray)
    {
        TempImage = Alloc_2D_UCHAR(Gray_Image_Width, Gray_Image_Height);
        EWWidth  = Gray_Image_Width;
        EWHeight = Gray_Image_Height;
    }
    else
    {
        TempImage = Alloc_2D_UCHAR(pEWbmpImage->Width, pEWbmpImage->Height);
        EWWidth  = pEWbmpImage->Width;
        EWHeight = pEWbmpImage->Height;
    }
    // key
    if  (EWKImageType == Img_PGMGray)
    {
        TempImageKey = Alloc_2D_char(Gray_Image_Key_Width, Gray_Image_Key_Height);
        EWKeyWidth  = Gray_Image_Key_Width;
        EWKeyHeight = Gray_Image_Key_Height;
    }
    else
    {
        TempImageKey = Alloc_2D_char(pEWKeybmpImage->Width, pEWKeybmpImage->Height);
        EWKeyWidth  = pEWKeybmpImage->Width;
        EWKeyHeight = pEWKeybmpImage->Height;
    }
    Red = Alloc_2D_Int(AviWidth, AviHeight);
    Green = Alloc_2D_Int(AviWidth, AviHeight);
	Blue = Alloc_2D_Int(AviWidth, AviHeight);

	int wLL = AviWidth >> DWTLevels;
	int hLL = AviHeight >> DWTLevels;
	/*********************************/
	/* Determine Incriment positions */
	DWTSegmentAdrs[0].x   = 0;
	DWTSegmentAdrs[0].y   = 0;
	/****************/
	/*   Level=3    */
	DWTSegmentAdrs[1].x   = 0;
	DWTSegmentAdrs[1].y   = hLL;

   	DWTSegmentAdrs[2].x   = wLL;
    DWTSegmentAdrs[2].y   = 0;

  	DWTSegmentAdrs[3].x   = wLL;
    DWTSegmentAdrs[3].y   = hLL;
    szSeg = 4;
    if (DWTLevels > 1)
    {
    	/****************/
	    /*   Level=2    */
    	DWTSegmentAdrs[4].x   = 0;                  //  South - West
	    DWTSegmentAdrs[4].y   = hLL<<1; // hLL*2

       	DWTSegmentAdrs[5].x   = wLL<<1;            // North - East
        DWTSegmentAdrs[5].y   = 0;

    	DWTSegmentAdrs[6].x   = wLL<<1;            // Diagonal
	    DWTSegmentAdrs[6].y   = hLL<<1; // hLL * 2
        szSeg = 7;
        if (DWTLevels == 3)
        {
        	/****************/
	        /*   Level=1    */
	        DWTSegmentAdrs[7].x   = 0;
        	DWTSegmentAdrs[7].y   = hLL<<2;

    	    DWTSegmentAdrs[8].x   = wLL<<2;
    	    DWTSegmentAdrs[8].y   = 0;

        	DWTSegmentAdrs[9].x   = wLL<<2;
    	    DWTSegmentAdrs[9].y   = hLL<<2;
        	/*********************************/
            szSeg = 10;
        }
    }
    // Determine embed starting and ending positions.
    // Only supports embedding in Level 1 or 2 after DWT_Level_2 or DWT_Level_3 transform
    if ((DWTLevels == 2) && (LevelEmbed == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }
    else if ((DWTLevels == 2) && (LevelEmbed == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[1].x;
        StartEmbedHorH = DWTSegmentAdrs[1].y;
        StartEmbedVerW = DWTSegmentAdrs[2].x;
        StartEmbedVerH = DWTSegmentAdrs[2].y;
        EmbedHorW = StartEmbedHorW + wLL;
        EmbedHorH = StartEmbedHorH + hLL;
        EmbedVerW = StartEmbedVerW + wLL;
        EmbedVerH = StartEmbedVerH + hLL;
    }
    else if ((DWTLevels == 3) && (LevelEmbed == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[7].x;
        StartEmbedHorH = DWTSegmentAdrs[7].y;
        StartEmbedVerW = DWTSegmentAdrs[8].x;
        StartEmbedVerH = DWTSegmentAdrs[8].y;
        EmbedHorW = StartEmbedHorW + wLL * 4;
        EmbedHorH = StartEmbedHorH + hLL * 4;
        EmbedVerW = StartEmbedVerW + wLL * 4;
        EmbedVerH = StartEmbedVerH + hLL * 4;
    }
    else if ((DWTLevels == 3) && (LevelEmbed == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }

    // don't scale watermark image. we don't need.
    if (EWImageType == Img_PGMGray)
    {
        // scale watermark image to
        for(i=0; i < EWHeight; i++)
        {
            for(j=0; j < EWWidth; j++)
            {
                TempImage[i][j] =  Gray_Image[i][j];
            }
        }
    }
    else
    {
       // scale watermark image to
        for(i=0; i < EWHeight; i++)
        {
            for(j=0; j < EWWidth; j++)
            {
                TempImage[i][j] = (UCHAR)(pEWbmpImage->Canvas->Pixels[j][i] & 0xFF);
            }
        }
    }

    // Scale Key Image to 0-1
    if (EWKImageType == Img_PGMGray)
    {
        // scale watermark image to
        for(i=0; i < EWKeyHeight; i++)
        {
            for(j=0; j < EWKeyWidth; j++)
            {
                //TempImageKey[i][j] = (double)Gray_Image_Key[i][j] / 255.0;
                TempImageKey[i][j] = Gray_Image_Key[i][j];
            }
        }
    }
    else
    {
       // scale watermark image to
        for(i=0; i < EWKeyHeight; i++)
        {
            for(j=0; j < EWKeyWidth; j++)
            {
                //TempImageKey[i][j] = (double)( pEWKeybmpImage->Canvas->Pixels[j][i] & 0xFF )  / 255.0;
                TempImageKey[i][j] = (ULONG)pEWKeybmpImage->Canvas->Pixels[j][i] & 0xFF;
            }
        }
    }

    for(i=0; i < EWKeyHeight; i++)
    {
        for(j=0; j < EWKeyWidth; j++)
        {
            if (TempImageKey[i][j] == 0)
                TempImageKey[i][j] = -1;
            else
                TempImageKey[i][j] = 1;
        }
    }

    int tmpImGGG;
    int aaH, abH, baH, bbH, aaW, abW, baW, bbW;
    int ADD1, ADD2, ADD3, ADD4;

    FormEmbedVideo->ButtonCancel->Enabled = true;
    FormEmbedVideo->ButtonEmbed->Enabled = false;
    FormEmbedVideo->LabelBlink->Visible = true;
    Application->ProcessMessages();
    for(iFrm=0; iFrm<NumFrames; iFrm++)  // for each frame
    {
        // Read Video
        RDGetFrame_Int(AviIn, iFrm, Red, Green, Blue);
        // DWT Transform
		l97_2D(Red, AviWidth, AviHeight, DWTLevels, false);
		l97_2D(Green, AviWidth, AviHeight, DWTLevels, false);
		l97_2D(Blue, AviWidth, AviHeight, DWTLevels, false);

        // EMBED the image
        for(i=0; i < EWHeight; i++)
        {
            for(j=0; j < EWWidth; j++)
            {
                tmpImGGG = TempImage[i][j];
                if (tmpImGGG > 0 )
                    tmpImGGG = -1;
                else
                    tmpImGGG = 1;
                is = i<<1; // i*2;
                js = j<<1; // j*2;

                // Horizontal scan positions
                aaH = StartEmbedHorH + is; // + 0
                abH = StartEmbedHorH + is + 1;
                aaW = StartEmbedHorW + js; // + 0
                abW = StartEmbedHorW + js + 1;
                // Vertical scan positions
                baH = StartEmbedVerH + is; // + 0
                bbH = StartEmbedVerH + is + 1;
                baW = StartEmbedVerW + js; // + 0
                bbW = StartEmbedVerW + js + 1;

                // calculate affect factors
                ADD1 = GainFactor * TempImageKey[is][js] * tmpImGGG;
                ADD2 = GainFactor * TempImageKey[is][js+1] * tmpImGGG;
                ADD3 = GainFactor * TempImageKey[is+1][js] * tmpImGGG;
                ADD4 = GainFactor * TempImageKey[is+1][js+1] * tmpImGGG;

                //  RED
                // Horizontal Scan
                Red[aaH][aaW] += ADD1;
                Red[aaH][abW] += ADD2;
                Red[abH][aaW] += ADD3;
                Red[abH][abW] += ADD4;
                // Vertical Scan
                Red[baH][baW] +=  ADD1;
                Red[baH][bbW] +=  ADD2;
                Red[bbH][baW] +=  ADD3;
                Red[bbH][bbW] +=  ADD4;

                // GREEN
                // Horizontal Scan
                Green[aaH][aaW] += ADD1;
                Green[aaH][abW] += ADD2;
                Green[abH][aaW] += ADD3;
                Green[abH][abW] += ADD4;
                // Vertical Scan
                Green[baH][baW] += ADD1;
                Green[baH][bbW] += ADD2;
                Green[bbH][baW] += ADD3;
                Green[bbH][bbW] += ADD4;

                // BLUE
                // Horizontal Scan
                Blue[aaH][aaW] += ADD1;
                Blue[aaH][abW] += ADD2;
                Blue[abH][aaW] += ADD3;
                Blue[abH][abW] += ADD4;
                // Vertical Scan
                Blue[baH][baW] += ADD1;
                Blue[baH][bbW] += ADD2;
                Blue[bbH][baW] += ADD3;
                Blue[bbH][bbW] += ADD4;

            } // j
        } // i

    	/*  Inverse wavelet transform.Back to Image matrix  */
		l97_2D(Red, AviWidth, AviHeight, DWTLevels, true);
		l97_2D(Green, AviWidth, AviHeight, DWTLevels, true);
		l97_2D(Blue, AviWidth, AviHeight, DWTLevels, true);

        /* WRITE Image to NEW AVI File */
		AddAviFrameMatrix_Int(AviOut, AviWidth, AviHeight, Red, Green, Blue);

        FormEmbedVideo->LabelEmbed->Caption = IntToStr(iFrm+1) + "/" + IntToStr(NumFrames) + " frames";
        FormEmbedVideo->BarEmbed->Percent = ((iFrm+1)*100)/NumFrames;
        Application->ProcessMessages();
        if (FormEmbedVideo->CancelEmbed)
        {
            FormEmbedVideo->ButtonCancel->Enabled = false;
            FormEmbedVideo->ButtonEmbed->Enabled = true;
            FormEmbedVideo->LabelBlink->Visible = false;

            StreamCopyAudio(AviIn, AviOut);

            // Close and Free all handles
            Free_2D_UCHAR(TempImage, EWHeight);
            Free_2D_char(TempImageKey, EWKeyHeight);
            Free_2D_Int(Red, AviHeight);
            Free_2D_Int(Green, AviHeight);
		    Free_2D_Int(Blue, AviHeight);
            RDCloseAvi(AviIn);
            CloseAvi(AviOut);
		    inAvi = NULL;
            outAvi = NULL;
            return true;
        }
        Application->ProcessMessages();
    }
    FormEmbedVideo->ButtonEmbed->Enabled = true;
    FormEmbedVideo->ButtonCancel->Enabled = false;
    FormEmbedVideo->LabelBlink->Visible = false;

    StreamCopyAudio(AviIn, AviOut);

    // Close and Free all handles
    Free_2D_UCHAR(TempImage, EWHeight);
    Free_2D_char(TempImageKey, EWKeyHeight);
    Free_2D_Int(Red, AviHeight);
    Free_2D_Int(Green, AviHeight);
	Free_2D_Int(Blue, AviHeight);
    RDCloseAvi(AviIn);
    CloseAvi(AviOut);
	inAvi = NULL;
    outAvi = NULL;
    return true;
}

/*
    Detect AVI Video
*/
int RecoverWatermarkAVI_Lifting( HWND thisHwnd,
                                AnsiString inVideoFileName,
                                // watermark inputs
                                Graphics::TBitmap *pRWbmpImage,
                                stcImageType RWImageType,
	                            UCHAR **Gray_Image_RW,
                                long Gray_Image_RW_Width,
                                long Gray_Image_RW_Height,
                                // Key Image inputs
                                Graphics::TBitmap *pRWKeybmpImage,
                                stcImageType RWKImageType,
        	                    UCHAR **Gray_Image_RW_Key,
                                long Gray_Image_RW_Key_Width,
                                long Gray_Image_RW_Key_Height,
                                // embed inputs
                                double BetaFactorRW,
                                int DWTLevelsRW, // wavelet tansform level
                                int LevelEmbedRW,  // in which level watermark has been embedded
                                bool AllFrames,
                                long FrameNo)
{

    long AviWidth, AviHeight, i, j, iImg, jImg, iImgKey, jImgKey, NumFrames, iFrm, is, js;
    long tmpW, tmpH, RWWidth, RWHeight, RWKeyWidth, RWKeyHeight;
    long EmbedHorW, EmbedHorH, EmbedVerW, EmbedVerH;
    long StartEmbedHorW, StartEmbedHorH, StartEmbedVerW, StartEmbedVerH;
	TAviUtil *inAvi;
	HAVI AviIn;
    UCHAR **TempImage;
    char **TempImageKey;
    int **Red, **Green, **Blue;
    BlockInfoStrc DWTSegmentAdrs[10];
    int szSeg;
    Graphics::TBitmap *pBitmapTMP;

    int tmpImGGG;
    int aaH, abH, baH, bbH, aaW, abW, baW, bbW;
    double mA1, mA2, mcorr, corr, corr1, corr2, A11, A12, A13, A14, A21, A22, A23, A24, mB, B, B1, B2, B3, B4;
    double tmp, tmp1, tmp2, tmp3, fThreshold, mCorrRed, mCorrGreen, mCorrBlue, WatCorr, WatBER;
    double  *tmpCorrM;
    TListItem *ListItem;
    AnsiString strTmp;

    // ------------------------
    // CHECK 1
    // there must be two images
    if ((RWKImageType == Img_NONE))
    {
        MessageDlg("You must select a Watermark and Key image.", mtError, TMsgDlgButtons() << mbOK, 0);
        return false;
    }

    // -----------------------------------------------------------
    // CHECK 2
    // Key image sizes MUST be equal to video DWT level size.
	// Open the AVI file and set handle
	AviIn = RDOpenAviFile(inVideoFileName.c_str(), true);
	if (AviIn == NULL)
	{
		DispError("ERROR! Cannot open AVI file.");
		return S_FALSE;
	}
	inAvi = (TAviUtil*)AviIn;
    AviWidth = inAvi->iWidth;
    AviHeight = inAvi->iHeight;
    NumFrames = inAvi->numframes;
    if ( DWTLevelsRW < LevelEmbedRW )
    {
        MessageDlg("DWT Levels cannot be smaller than Embed Levels.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
    }
    // Check the dimensions for compatability.
    if (AviWidth%(1 << DWTLevelsRW) || AviHeight%(1 << DWTLevelsRW))
	{
        AnsiString sss = "width and height of input video must be divisible by 2^" + IntToStr(DWTLevelsRW) + " = " + IntToStr((int)pow(2, DWTLevelsRW));
        MessageDlg(sss, mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
        inAvi = NULL;
        return false;
	}
    if ((DWTLevelsRW == 2) || (DWTLevelsRW == 3))
    {
        if (LevelEmbedRW == 1)
        {
            tmpW = AviWidth >> 1; // division by 2
            tmpH = AviHeight >> 1;
        }
        else if (LevelEmbedRW == 2)
        {
            tmpW = AviWidth >> 2; // division by 4
            tmpH = AviHeight >> 2;
        }
        else
        {
            MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
            RDCloseAvi(AviIn);
    	    inAvi = NULL;
            return false;
        }
    }
    else
    {
        MessageDlg("This software supports maximum 3-level DWT and level-1 or level-2 embeding.", mtError, TMsgDlgButtons() << mbOK, 0);
        RDCloseAvi(AviIn);
	    inAvi = NULL;
        return false;
    }
    // if we came this far, inAvi structure is still open.
    // We will continue to use this structure.


    /* Allocate required buffers */
    // original watermark
    if (RWImageType == Img_PGMGray)
    {
        TempImage = Alloc_2D_UCHAR(Gray_Image_RW_Width, Gray_Image_RW_Height);
        RWWidth  = Gray_Image_RW_Width;
        RWHeight = Gray_Image_RW_Height;
    }
    else if (RWImageType != Img_NONE)
    {
        TempImage = Alloc_2D_UCHAR(pRWbmpImage->Width, pRWbmpImage->Height);
        RWWidth  = pRWbmpImage->Width;
        RWHeight = pRWbmpImage->Height;
    }
    // key
    if  (RWKImageType == Img_PGMGray)
    {
        TempImageKey = Alloc_2D_char(Gray_Image_RW_Key_Width, Gray_Image_RW_Key_Height);
        RWKeyWidth  = Gray_Image_RW_Key_Width;
        RWKeyHeight = Gray_Image_RW_Key_Height;
    }
    else
    {
        TempImageKey = Alloc_2D_char(pRWKeybmpImage->Width, pRWKeybmpImage->Height);
        RWKeyWidth  = pRWKeybmpImage->Width;
        RWKeyHeight = pRWKeybmpImage->Height;
    }

    Red = Alloc_2D_Int(AviWidth, AviHeight);
    Green = Alloc_2D_Int(AviWidth, AviHeight);
	Blue = Alloc_2D_Int(AviWidth, AviHeight);



	int wLL = AviWidth >> DWTLevelsRW;
	int hLL = AviHeight >> DWTLevelsRW;
	/*********************************/
	/* Determine Incriment positions */
	DWTSegmentAdrs[0].x   = 0;
	DWTSegmentAdrs[0].y   = 0;
	/****************/
	/*   Level=3    */
	DWTSegmentAdrs[1].x   = 0;
	DWTSegmentAdrs[1].y   = hLL;

   	DWTSegmentAdrs[2].x   = wLL;
    DWTSegmentAdrs[2].y   = 0;

  	DWTSegmentAdrs[3].x   = wLL;
    DWTSegmentAdrs[3].y   = hLL;
    szSeg = 4;
    if (DWTLevelsRW > 1)
    {
    	/****************/
	    /*   Level=2    */
    	DWTSegmentAdrs[4].x   = 0;                  //  South - West
	    DWTSegmentAdrs[4].y   = hLL<<1; // hLL*2

       	DWTSegmentAdrs[5].x   = wLL<<1;            // North - East
        DWTSegmentAdrs[5].y   = 0;

    	DWTSegmentAdrs[6].x   = wLL<<1;            // Diagonal
	    DWTSegmentAdrs[6].y   = hLL<<1; // hLL * 2
        szSeg = 7;
        if (DWTLevelsRW == 3)
        {
        	/****************/
	        /*   Level=1    */
	        DWTSegmentAdrs[7].x   = 0;
        	DWTSegmentAdrs[7].y   = hLL<<2;

    	    DWTSegmentAdrs[8].x   = wLL<<2;
    	    DWTSegmentAdrs[8].y   = 0;

        	DWTSegmentAdrs[9].x   = wLL<<2;
    	    DWTSegmentAdrs[9].y   = hLL<<2;
        	/*********************************/
            szSeg = 10;
        }
    }
    // Determine embed starting and ending positions.
    // Only supports embedding in Level 1 or 2 after DWT_Level_2 or DWT_Level_3 transform
    if ((DWTLevelsRW == 2) && (LevelEmbedRW == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }
    else if ((DWTLevelsRW == 2) && (LevelEmbedRW == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[1].x;
        StartEmbedHorH = DWTSegmentAdrs[1].y;
        StartEmbedVerW = DWTSegmentAdrs[2].x;
        StartEmbedVerH = DWTSegmentAdrs[2].y;
        EmbedHorW = StartEmbedHorW + wLL;
        EmbedHorH = StartEmbedHorH + hLL;
        EmbedVerW = StartEmbedVerW + wLL;
        EmbedVerH = StartEmbedVerH + hLL;
    }
    else if ((DWTLevelsRW == 3) && (LevelEmbedRW == 1))
    {
        StartEmbedHorW = DWTSegmentAdrs[7].x;
        StartEmbedHorH = DWTSegmentAdrs[7].y;
        StartEmbedVerW = DWTSegmentAdrs[8].x;
        StartEmbedVerH = DWTSegmentAdrs[8].y;
        EmbedHorW = StartEmbedHorW + wLL * 4;
        EmbedHorH = StartEmbedHorH + hLL * 4;
        EmbedVerW = StartEmbedVerW + wLL * 4;
        EmbedVerH = StartEmbedVerH + hLL * 4;
    }
    else if ((DWTLevelsRW == 3) && (LevelEmbedRW == 2))
    {
        StartEmbedHorW = DWTSegmentAdrs[4].x;
        StartEmbedHorH = DWTSegmentAdrs[4].y;
        StartEmbedVerW = DWTSegmentAdrs[5].x;
        StartEmbedVerH = DWTSegmentAdrs[5].y;
        EmbedHorW = StartEmbedHorW + wLL * 2;
        EmbedHorH = StartEmbedHorH + hLL * 2;
        EmbedVerW = StartEmbedVerW + wLL * 2;
        EmbedVerH = StartEmbedVerH + hLL * 2;
    }

    // fill temp watermark image matrix
    if (RWImageType == Img_PGMGray)
    {
        for(i=0; i < RWHeight; i++)
        {
            for(j=0; j < RWWidth; j++)
            {
                TempImage[i][j] =  Gray_Image_RW[i][j];
            }
        }
    }
    else if (RWImageType != Img_NONE)
    {
        for(i=0; i < RWHeight; i++)
        {
            for(j=0; j < RWWidth; j++)
            {
                TempImage[i][j] = (UCHAR)(pRWbmpImage->Canvas->Pixels[j][i] & 0xFF);
            }
        }
    }
    // fill temp key image matrix
    if (RWKImageType == Img_PGMGray)
    {
        for(i=0; i < RWKeyHeight; i++)
        {
            for(j=0; j < RWKeyWidth; j++)
            {
                TempImageKey[i][j] = Gray_Image_RW_Key[i][j];
            }
        }
    }
    else
    {
        for(i=0; i < RWKeyHeight; i++)
        {
            for(j=0; j < RWKeyWidth; j++)
            {
                TempImageKey[i][j] = (ULONG)pRWKeybmpImage->Canvas->Pixels[j][i] & 0xFF;
            }
        }
    }

    for(i=0; i < RWKeyHeight; i++)
    {
        for(j=0; j < RWKeyWidth; j++)
        {
            if (TempImageKey[i][j] == 0)
                TempImageKey[i][j] = -1;
            else
                TempImageKey[i][j] = 1;
        }
    }


    // temporary recover image
    pBitmapTMP = new Graphics::TBitmap();
    pBitmapTMP->PixelFormat = pf24bit;
    pBitmapTMP->Width = RWKeyWidth/2;
    pBitmapTMP->Height = RWKeyHeight/2;
    TCanvas *tmpCanvas = pBitmapTMP->Canvas;

    if (!AllFrames)   // only one frame
    {
        FormRecover->ButtonCancel->Enabled = true;
        FormRecover->ButtonRecover->Enabled = false;
    }
    else
    {
        FormRecover->ButtonCancel->Enabled = true;
        FormRecover->ButtonCalcBER->Enabled = false;
    }
    FormRecover->GroupBoxOptions->Enabled = false;
    FormRecover->ListResult->Items->Clear();
    Application->ProcessMessages();

    tmpCorrM = (double *)malloc((RWKeyWidth/2) * (RWKeyHeight/2) * sizeof(double));

    for(iFrm=0; iFrm<NumFrames; iFrm++)  // for each frame
    {
        // Read Video
        if (!AllFrames)  // only one frame
        {
            RDGetFrame_Int(AviIn, FrameNo, Red, Green, Blue);
        }
        else  // all frames
        {
            RDGetFrame_Int(AviIn, iFrm, Red, Green, Blue);
        }

        // DWT Transform
    	l97_2D(Red, AviWidth, AviHeight, DWTLevelsRW, false);
		l97_2D(Green,AviWidth, AviHeight, DWTLevelsRW, false);
		l97_2D(Blue, AviWidth, AviHeight, DWTLevelsRW, false);

        corr = 0.0;
        for(i=0; i < RWKeyHeight/2; i++)
        {
            for(j=0; j < RWKeyWidth/2; j++)
            {
                is = i*2;
                js = j*2;
                // Horizontal scan positions
                aaH = StartEmbedHorH + is; // + 0
                abH = StartEmbedHorH + is + 1;
                aaW = StartEmbedHorW + js; // + 0
                abW = StartEmbedHorW + js + 1;
                // Vertical scan positions
                baH = StartEmbedVerH + is; // + 0
                bbH = StartEmbedVerH + is + 1;
                baW = StartEmbedVerW + js; // + 0
                bbW = StartEmbedVerW + js + 1;

                //  RED
                // image matrix elements
                A11 = Red[aaH][aaW];
                A12 = Red[aaH][abW];
                A13 = Red[abH][aaW];
                A14 = Red[abH][abW];
                mA1 = (A11+A12+A13+A14)/4.0;

                A21 = Red[baH][baW];
                A22 = Red[baH][bbW];
                A23 = Red[bbH][baW];
                A24 = Red[bbH][bbW];
                mA2 = (A21+A22+A23+A24)/4.0;

                B1 = TempImageKey[is][js];
                B2 = TempImageKey[is][js+1];
                B3 = TempImageKey[is+1][js];
                B4 = TempImageKey[is+1][js+1];
                mB = (B1 + B2 + B3 + B4)/ 4.0;

                // Horizontal Scan
                tmp1 = (A11-mA1)*(B1 - mB) + (A12-mA1)*(B2 - mB) + (A13-mA1)*(B3 - mB) + (A14-mA1)*(B4 - mB);
                tmp2 = (A11-mA1)*(A11-mA1) + (A12-mA1)*(A12-mA1) + (A13-mA1)*(A13-mA1) + (A14-mA1)*(A14-mA1);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr1 = 0;
                else
                    corr1 = (tmp1)/(sqrt(tmp2*tmp3));

                // Vertical Scan
                tmp1 = (A21-mA2)*(B1 - mB) + (A22-mA2)*(B2 - mB) + (A23-mA2)*(B3 - mB) + (A24-mA2)*(B4 - mB);
                tmp2 = (A21-mA2)*(A21-mA2) + (A22-mA2)*(A22-mA2) + (A23-mA2)*(A23-mA2) + (A24-mA2)*(A24-mA2);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr2 = 0;
                else
                    corr2 = (tmp1)/(sqrt(tmp2*tmp3));

                mCorrRed = (corr1 + corr2)/2.0;

                //  GREEN
                // image matrix elements
                A11 = Green[aaH][aaW];
                A12 = Green[aaH][abW];
                A13 = Green[abH][aaW];
                A14 = Green[abH][abW];
                mA1 = (A11+A12+A13+A14)/4.0;

                A21 = Green[baH][baW];
                A22 = Green[baH][bbW];
                A23 = Green[bbH][baW];
                A24 = Green[bbH][bbW];
                mA2 = (A21+A22+A23+A24)/4.0;

                B1 = TempImageKey[is][js];
                B2 = TempImageKey[is][js+1];
                B3 = TempImageKey[is+1][js];
                B4 = TempImageKey[is+1][js+1];
                mB = (B1 + B2 + B3 + B4)/ 4.0;

                // Horizontal Scan
                tmp1 = (A11-mA1)*(B1 - mB) + (A12-mA1)*(B2 - mB) + (A13-mA1)*(B3 - mB) + (A14-mA1)*(B4 - mB);
                tmp2 = (A11-mA1)*(A11-mA1) + (A12-mA1)*(A12-mA1) + (A13-mA1)*(A13-mA1) + (A14-mA1)*(A14-mA1);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr1 = 0;
                else
                    corr1 = (tmp1)/(sqrt(tmp2*tmp3));
                // Vertical Scan
                tmp1 = (A21-mA2)*(B1 - mB) + (A22-mA2)*(B2 - mB) + (A23-mA2)*(B3 - mB) + (A24-mA2)*(B4 - mB);
                tmp2 = (A21-mA2)*(A21-mA2) + (A22-mA2)*(A22-mA2) + (A23-mA2)*(A23-mA2) + (A24-mA2)*(A24-mA2);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr2 = 0;
                else
                    corr2 = (tmp1)/(sqrt(tmp2*tmp3));

                mCorrGreen = (corr1 + corr2)/2.0;


                //  BLUE
                // image matrix elements
                A11 = Blue[aaH][aaW];
                A12 = Blue[aaH][abW];
                A13 = Blue[abH][aaW];
                A14 = Blue[abH][abW];
                mA1 = (A11+A12+A13+A14)/4.0;

                A21 = Blue[baH][baW];
                A22 = Blue[baH][bbW];
                A23 = Blue[bbH][baW];
                A24 = Blue[bbH][bbW];
                mA2 = (A21+A22+A23+A24)/4.0;

                B1 = TempImageKey[is][js];
                B2 = TempImageKey[is][js+1];
                B3 = TempImageKey[is+1][js];
                B4 = TempImageKey[is+1][js+1];
                mB = (B1 + B2 + B3 + B4)/ 4.0;

                // Horizontal Scan
                tmp1 = (A11-mA1)*(B1 - mB) + (A12-mA1)*(B2 - mB) + (A13-mA1)*(B3 - mB) + (A14-mA1)*(B4 - mB);
                tmp2 = (A11-mA1)*(A11-mA1) + (A12-mA1)*(A12-mA1) + (A13-mA1)*(A13-mA1) + (A14-mA1)*(A14-mA1);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr1 = 0;
                else
                    corr1 = (tmp1)/(sqrt(tmp2*tmp3));
                // Vertical Scan
                tmp1 = (A21-mA2)*(B1 - mB) + (A22-mA2)*(B2 - mB) + (A23-mA2)*(B3 - mB) + (A24-mA2)*(B4 - mB);
                tmp2 = (A21-mA2)*(A21-mA2) + (A22-mA2)*(A22-mA2) + (A23-mA2)*(A23-mA2) + (A24-mA2)*(A24-mA2);
                tmp3 = (B1 - mB)*(B1 - mB) + (B2 - mB)*(B2 - mB) + (B3 - mB)*(B3 - mB) + (B4 - mB)*(B4 - mB);
                if ((tmp2 == 0.0) || (tmp3 == 0.0))
                    corr2 = 0;
                else
                    corr2 = (tmp1)/(sqrt(tmp2*tmp3));

                mCorrBlue = (corr1 + corr2)/2.0;

                // find the biggest correlation
                mcorr = mCorrRed;
                if (mCorrGreen > mcorr)
                    mcorr = mCorrGreen;
                if (mCorrBlue > mcorr)
                    mcorr = mCorrBlue;
                // add current correlation to total correlation
                tmpCorrM[i * RWKeyWidth/2 + j] = mcorr;
            } // j
        } // i

        // find mean correlation
        corr = 0.0;
        for(i=0; i < (RWKeyWidth * RWKeyHeight)/4; i++)
        {
            corr+= tmpCorrM[i];
        }
        mcorr = corr / (double)((RWKeyWidth * RWKeyHeight)/4.0);
        // find final theshold
        fThreshold = BetaFactorRW * mcorr;

        if (!AllFrames) // only one frame
        {
            // display image
            for(i=0; i < RWKeyHeight/2; i++)
            {
                for(j=0; j < RWKeyWidth/2; j++)
                {
                    if ( tmpCorrM[i * RWKeyWidth/2 + j] >= fThreshold)
                        tmpCanvas->Pixels[j][i] = (TColor)RGB(0, 0, 0);
                    else
                        tmpCanvas->Pixels[j][i] =   (TColor)RGB(255, 255, 255);
                }
            }
            FormRecover->ImageRecover->Picture->Assign(pBitmapTMP);
            FormRecover->ButtonSaveImage->Enabled = true;
            FormRecover->ButtonCancel->Enabled = false;
            FormRecover->ButtonRecover->Enabled = true;
            FormRecover->GroupBoxOptions->Enabled = true;

            // display BitErrors (BER)
            if (FormMain->RWImageType != Img_NONE)
            {
                // find BER and means
                WatBER = 0;
                for(i=0; i < RWHeight; i++)
                {
                    for(j=0; j < RWWidth; j++)
                    {
                        if ( (tmpCanvas->Pixels[j][i] & 0xFF ) != TempImage[i][j])
                           WatBER += 1;
                    }
                }
                WatBER = (WatBER * 100)/ (RWWidth * RWHeight);

                ListItem =  FormRecover->ListResult->Items->Add();
                // FrameNo
                ListItem->Caption = IntToStr(FrameNo);
                // BER
                strTmp.sprintf("%3.2f", WatBER);
                ListItem->SubItems->Add( strTmp );
            }
            delete pBitmapTMP;
            if (FormMain->wantRWCalcCorrelation)
                Free_2D_UCHAR(TempImage, RWHeight);  // watermark
            Free_2D_char(TempImageKey, RWKeyHeight);
            Free_2D_Int(Red, AviHeight);
            Free_2D_Int(Green, AviHeight);
		    Free_2D_Int(Blue, AviHeight);
            free(tmpCorrM);
            RDCloseAvi(AviIn);
		    inAvi = NULL;
            return true;
        }
        else   // all frames
        {
            FormRecover->LabelRecover->Caption = IntToStr(iFrm+1) + "/" + IntToStr(NumFrames) + " frames";
            FormRecover->BarRecover->Percent = ((iFrm+1)*100)/NumFrames;
            // add MER line into List
            // display BitErrors (BER)
            if (FormMain->RWImageType != Img_NONE)
            {
                // find BER and means
                WatBER = 0;
                for(i=0; i < RWHeight; i++)
                {
                    for(j=0; j < RWWidth; j++)
                    {
                        if ( tmpCorrM[i * RWKeyWidth/2 + j] >= fThreshold)
                            szSeg = 0;
                        else
                            szSeg = 255;
                        if ( szSeg != TempImage[i][j])
                           WatBER += 1;
                    }
                }
                WatBER = (WatBER * 100)/ (RWWidth * RWHeight);

                ListItem =  FormRecover->ListResult->Items->Add();
                // FrameNo
                ListItem->Caption = IntToStr(iFrm);
                // BER
                strTmp.sprintf("%3.2f", WatBER);
                FormRecover->recAllFrames = FormRecover->recAllFrames + strTmp + ", ";
                ListItem->SubItems->Add( strTmp );
            }
        }

        Application->ProcessMessages();
        if (FormRecover->cancelRecover)
        {
            if (!AllFrames)   // only one frame
            {
                FormRecover->ButtonCancel->Enabled = false;
                FormRecover->ButtonRecover->Enabled = true;
            }
            else
            {
                FormRecover->ButtonCancel->Enabled = false;
                FormRecover->ButtonCalcBER->Enabled = true;
            }
            FormRecover->GroupBoxOptions->Enabled = true;

            // Close and Free all handles
            if (FormMain->wantRWCalcCorrelation)
                Free_2D_UCHAR(TempImage, RWHeight);  // watermark
            Free_2D_char(TempImageKey, RWKeyHeight);
            Free_2D_Int(Red, AviHeight);
            Free_2D_Int(Green, AviHeight);
		    Free_2D_Int(Blue, AviHeight);
            free(tmpCorrM);
            RDCloseAvi(AviIn);
		    inAvi = NULL;
            return true;
        }
        Application->ProcessMessages();
    }
    if (!AllFrames) // only one frame
    {
        FormRecover->ButtonCancel->Enabled = false;
        FormRecover->ButtonRecover->Enabled = true;
    }
    else
    {
        FormRecover->ButtonCancel->Enabled = false;
        FormRecover->ButtonCalcBER->Enabled = true;
        FormRecover->ButtonSaveResults->Enabled = true;
    }
    FormRecover->GroupBoxOptions->Enabled = true;
    // Close and Free all handles
    if (FormMain->wantRWCalcCorrelation)
        Free_2D_UCHAR(TempImage, RWHeight); // watermark
    Free_2D_char(TempImageKey, RWKeyHeight);
    Free_2D_Int(Red, AviHeight);
    Free_2D_Int(Green, AviHeight);
	Free_2D_Int(Blue, AviHeight);
    free(tmpCorrM);
    RDCloseAvi(AviIn);
	inAvi = NULL;
    return true;
}