// FILE: tripodDlg.cpp : implementation file
// AUTH: P. Oh
// DATE: 04/24/02 04:00
// VERS: 11/21/01 : 0.9 posted on web
//       04/24/02 : 1.0 Added free to release memory malloced for m_destinationBmp
// DESC: TRIPOD template files for real-time image processing development
// STAT: 1.0 Working
// REFS: brinarize - Example application for binary images

#include "stdafx.h"
#include "tripod.h"
#include "tripodDlg.h"

#include "LVServerDefs.h"
#include "math.h"
#include <fstream>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

using namespace std;
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTripodDlg dialog

CTripodDlg::CTripodDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CTripodDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CTripodDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	//////////////// Set destination BMP to NULL first 
	m_destinationBitmapInfoHeader = NULL;
	m_destinationBitmapRightInfoHeader = NULL;

}

////////////////////// Additional generic functions

static unsigned PixelBytes(int w, int bpp)
{
    return (w * bpp + 7) / 8;
}

static unsigned DibRowSize(int w, int bpp)
{
    return (w * bpp + 31) / 32 * 4;
}

static unsigned DibRowSize(LPBITMAPINFOHEADER pbi)
{
    return DibRowSize(pbi->biWidth, pbi->biBitCount);
}

static unsigned DibRowPadding(int w, int bpp)
{
    return DibRowSize(w, bpp) - PixelBytes(w, bpp);
}

static unsigned DibRowPadding(LPBITMAPINFOHEADER pbi)
{
    return DibRowPadding(pbi->biWidth, pbi->biBitCount);
}

static unsigned DibImageSize(int w, int h, int bpp)
{
    return h * DibRowSize(w, bpp);
}

static size_t DibSize(int w, int h, int bpp)
{
    return sizeof (BITMAPINFOHEADER) + DibImageSize(w, h, bpp);
}

/////////////////////// end of generic functions


void CTripodDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTripodDlg)
	DDX_Control(pDX, IDC_PROCESSEDVIEW, m_cVideoProcessedView);
	DDX_Control(pDX, IDC_UNPROCESSEDVIEW, m_cVideoUnprocessedView);
	DDX_Control(pDX, IDC_PROCESSEDVIEWRIGHT, m_cVideoProcessedViewRight);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTripodDlg, CDialog)
	//{{AFX_MSG_MAP(CTripodDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDEXIT, OnExit)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTripodDlg message handlers

BOOL CTripodDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	center = 24;
	side = 4;
	side2 = 1;
	non = -4;
	countStart = 0;
	
	
	// Variables for using 2 cameras
	whichSide = false;
	wasDisplayed = false;

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here

	// For Unprocessed view videoportal (top one)
	char sRegUnprocessedView[] = "HKEY_LOCAL_MACHINE\\Software\\UnprocessedView";
	m_cVideoUnprocessedView.PrepareControl("UnprocessedView", sRegUnprocessedView, 0 );	
	m_cVideoUnprocessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoUnprocessedView.ConnectCamera(0);
	m_cVideoUnprocessedView.SetEnablePreview(TRUE);

	// For binary view videoportal (bottom one)
	char sRegProcessedView[] = "HKEY_LOCAL_MACHINE\\Software\\ProcessedView";
	m_cVideoProcessedView.PrepareControl("ProcessedView", sRegProcessedView, 0 );	
	m_cVideoProcessedView.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoProcessedView.ConnectCamera(0);
	m_cVideoProcessedView.SetEnablePreview(TRUE);

	// Initialize the size of binary videoportal
	m_cVideoProcessedView.SetPreviewMaxHeight(240);
	m_cVideoProcessedView.SetPreviewMaxWidth(320);

	// For binary view videoportal (bottom one)
	char sRegProcessedViewRight[] = "HKEY_LOCAL_MACHINE\\Software\\ProcessedViewRight";
	m_cVideoProcessedViewRight.PrepareControl("ProcessedViewReview", sRegProcessedViewRight, 0 );	
	m_cVideoProcessedViewRight.EnableUIElements(UIELEMENT_STATUSBAR,0,TRUE);
	m_cVideoProcessedViewRight.ConnectCamera(0);
	m_cVideoProcessedViewRight.SetEnablePreview(TRUE);

	// Initialize the size of binary videoportal
	m_cVideoProcessedViewRight.SetPreviewMaxHeight(240);
	m_cVideoProcessedViewRight.SetPreviewMaxWidth(320);

	// Uncomment if you wish to fix the live videoportal's size
	// m_cVideoUnprocessedView.SetPreviewMaxHeight(240);
	// m_cVideoUnprocessedView.SetPreviewMaxWidth(320);

	// Find the screen coodinates of the binary videoportal
	m_cVideoProcessedView.GetWindowRect(m_rectForProcessedView);
	ScreenToClient(m_rectForProcessedView);
	allocateDib(CSize(320, 240));

	// Find the screen coodinates of the binary videoportal
	m_cVideoProcessedViewRight.GetWindowRect(m_rectForProcessedViewRight);
	ScreenToClient(m_rectForProcessedViewRight);
	allocateDib(CSize(320, 240));

	// Start grabbing frame data for Procssed videoportal (bottom one)
	m_cVideoProcessedView.StartVideoHook(0);

	// Start grabbing frame data for Procssed videoportal (bottom one)
	m_cVideoProcessedViewRight.StartVideoHook(0);
	
	SetTimer(1, 540, NULL);

	// Get Masks
	getMaskBmp(0);
	getMaskBmp(1);
	getMaskBmp(2);
	getMaskBmp(3);

	return TRUE;  // return TRUE  unless you set the focus to a control

	
}

void CTripodDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTripodDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}

}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTripodDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CTripodDlg::OnExit() 
{
	// TODO: Add your control notification handler code here

	// Kill live view videoportal (top one)
	m_cVideoUnprocessedView.StopVideoHook(0);
    m_cVideoUnprocessedView.DisconnectCamera();	
	
	// Kill binary view videoportal (bottom left one)
	m_cVideoProcessedView.StopVideoHook(0);
    m_cVideoProcessedView.DisconnectCamera();	

	// Kill binary view videoportal (bottom right one)
	m_cVideoProcessedViewRight.StopVideoHook(0);
    m_cVideoProcessedViewRight.DisconnectCamera();

	// Kill program
	DestroyWindow();	

}

BEGIN_EVENTSINK_MAP(CTripodDlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CTripodDlg)
	ON_EVENT(CTripodDlg, IDC_PROCESSEDVIEW, 1 /* PortalNotification */, OnPortalNotificationProcessedview, VTS_I4 VTS_I4 VTS_I4 VTS_I4)
	ON_EVENT(CTripodDlg, IDC_PROCESSEDVIEWRIGHT, 1 /* PortalNotification */, OnPortalNotificationProcessedviewRight, VTS_I4 VTS_I4 VTS_I4 VTS_I4)
	//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CTripodDlg::OnPortalNotificationProcessedview(long lMsg, long lParam1, long lParam2, long lParam3) 
{
	// TODO: Add your control notification handler code here
	
	// This function is called at the camera's frame rate
    
#define NOTIFICATIONMSG_VIDEOHOOK	10

	// Declare some useful variables
	// QCSDKMFC.pdf (Quickcam MFC documentation) p. 103 explains the variables lParam1, lParam2, lParam3 too 


	//LPBITMAPINFOHEADER lpBitmapInfoHeader; // Frame's info header contains info like width and height
	//LPBYTE lpBitmapPixelData; // This pointer-to-long will point to the start of the frame's pixel data
    //unsigned long lTimeStamp; // Time when frame was grabbed

	switch(lMsg) {
		case NOTIFICATIONMSG_VIDEOHOOK:
			{
				if(whichSide == 0)
				{
					lpBitmapInfoHeader = (LPBITMAPINFOHEADER) lParam1; 
					lpBitmapPixelData = (LPBYTE) lParam2;
					lTimeStamp = (unsigned long) lParam3;
	
					//grayScaleTheFrameData(lpBitmapInfoHeader, lpBitmapPixelData);
					//doMyImageProcessing(lpBitmapInfoHeader); // Place where you'd add your image processing code
					//displayMyResults(lpBitmapInfoHeader);
				}
			}
			break;

		default:
			break;
	}	

	wasDisplayed = true;
}

void CTripodDlg::OnPortalNotificationProcessedviewRight(long lMsg, long lParam1, long lParam2, long lParam3) 
{
	// TODO: Add your control notification handler code here
	
	// This function is called at the camera's frame rate
    
#define NOTIFICATIONMSG_VIDEOHOOK2	10

	// Declare some useful variables
	// QCSDKMFC.pdf (Quickcam MFC documentation) p. 103 explains the variables lParam1, lParam2, lParam3 too 
	

	switch(lMsg) {
		case NOTIFICATIONMSG_VIDEOHOOK2:
			{
				if(whichSide == 1)
				{
					lpBitmapInfoHeader = (LPBITMAPINFOHEADER) lParam1; 
					lpBitmapPixelData = (LPBYTE) lParam2;
					lTimeStamp = (unsigned long) lParam3;
				}
			}
			break;

		default:
			break;
	}	
	wasDisplayed = true;
}

void CTripodDlg::allocateDib(CSize sz)
{
	// Purpose: allocate information for a device independent bitmap (DIB)
	// Called from OnInitVideo

	if(m_destinationBitmapInfoHeader) {
		free(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader = NULL;
	}

	if(sz.cx | sz.cy) {
		m_destinationBitmapInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
		ASSERT(m_destinationBitmapInfoHeader);
		m_destinationBitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
		m_destinationBitmapInfoHeader->biWidth = sz.cx;
		m_destinationBitmapInfoHeader->biHeight = sz.cy;
		m_destinationBitmapInfoHeader->biPlanes = 1;
		m_destinationBitmapInfoHeader->biBitCount = 24;
		m_destinationBitmapInfoHeader->biCompression = 0;
		m_destinationBitmapInfoHeader->biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
		m_destinationBitmapInfoHeader->biXPelsPerMeter = 0;
		m_destinationBitmapInfoHeader->biYPelsPerMeter = 0;
		m_destinationBitmapInfoHeader->biClrImportant = 0;
		m_destinationBitmapInfoHeader->biClrUsed = 0;
	}

	if(m_destinationBitmapRightInfoHeader) {
		free(m_destinationBitmapRightInfoHeader);
		m_destinationBitmapRightInfoHeader = NULL;
	}

	if(sz.cx | sz.cy) {
		m_destinationBitmapRightInfoHeader = (LPBITMAPINFOHEADER)malloc(DibSize(sz.cx, sz.cy, 24));
		ASSERT(m_destinationBitmapRightInfoHeader);
		m_destinationBitmapRightInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
		m_destinationBitmapRightInfoHeader->biWidth = sz.cx;
		m_destinationBitmapRightInfoHeader->biHeight = sz.cy;
		m_destinationBitmapRightInfoHeader->biPlanes = 1;
		m_destinationBitmapRightInfoHeader->biBitCount = 24;
		m_destinationBitmapRightInfoHeader->biCompression = 0;
		m_destinationBitmapRightInfoHeader->biSizeImage = DibImageSize(sz.cx, sz.cy, 24);
		m_destinationBitmapRightInfoHeader->biXPelsPerMeter = 0;
		m_destinationBitmapRightInfoHeader->biYPelsPerMeter = 0;
		m_destinationBitmapRightInfoHeader->biClrImportant = 0;
		m_destinationBitmapRightInfoHeader->biClrUsed = 0;
	}
}

void CTripodDlg::displayMyResults(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
	// displayMyResults: Displays results of doMyImageProcessing() in the videoport
	// Notes: StretchDIBits stretches a device-independent bitmap to the appropriate size

	CDC				*pDC;	// Device context to display bitmap data
	
	pDC = GetDC();	
	int nOldMode = SetStretchBltMode(pDC->GetSafeHdc(),COLORONCOLOR);

	StretchDIBits( 
		pDC->GetSafeHdc(),
		m_rectForProcessedView.left,				// videoportal left-most coordinate
		m_rectForProcessedView.top,					// videoportal top-most coordinate
		m_rectForProcessedView.Width(),				// videoportal width
		m_rectForProcessedView.Height(),			// videoportal height
		0,											// Row position to display bitmap in videoportal
		0,											// Col position to display bitmap in videoportal
		lpThisBitmapInfoHeader->biWidth,			// m_destinationBmp's number of columns
		lpThisBitmapInfoHeader->biHeight,			// m_destinationBmp's number of rows
		m_destinationBmp,							// The bitmap to display; use the one resulting from doMyImageProcessing
		(BITMAPINFO*)m_destinationBitmapInfoHeader, // The bitmap's header info e.g. width, height, number of bits etc
		DIB_RGB_COLORS,								// Use default 24-bit color table
		SRCCOPY										// Just display
	);
 
	SetStretchBltMode(pDC->GetSafeHdc(),nOldMode);

	ReleaseDC(pDC);


	// Note: 04/24/02 - Added the following:
	// Christopher Wagner cwagner@fas.harvard.edu noticed that memory wasn't being freed

	// Recall OnPortalNotificationProcessedview, which gets called everytime
	// a frame of data arrives, performs 3 steps:
	// (1) grayScaleTheFrameData - which mallocs m_destinationBmp
	// (2) doMyImageProcesing
	// (3) displayMyResults - which we're in now
	// Since we're finished with the memory we malloc'ed for m_destinationBmp
	// we should free it: 
	
	free(m_destinationBmp);

	// End of adds


}


void CTripodDlg::displayMyResultsRight(LPBITMAPINFOHEADER lpThisBitmapInfoHeader)
{
	// displayMyResults: Displays results of doMyImageProcessing() in the videoport
	// Notes: StretchDIBits stretches a device-independent bitmap to the appropriate size

	CDC				*pDC;	// Device context to display bitmap data
	
	pDC = GetDC();	
	int nOldMode = SetStretchBltMode(pDC->GetSafeHdc(),COLORONCOLOR);

	StretchDIBits( 
		pDC->GetSafeHdc(),
		m_rectForProcessedViewRight.left,				// videoportal left-most coordinate
		m_rectForProcessedViewRight.top,					// videoportal top-most coordinate
		m_rectForProcessedViewRight.Width(),				// videoportal width
		m_rectForProcessedViewRight.Height(),			// videoportal height
		0,											// Row position to display bitmap in videoportal
		0,											// Col position to display bitmap in videoportal
		lpThisBitmapInfoHeader->biWidth,			// m_destinationBmp's number of columns
		lpThisBitmapInfoHeader->biHeight,			// m_destinationBmp's number of rows
		m_destinationBmpRight,						// The bitmap to display; use the one resulting from doMyImageProcessing
		(BITMAPINFO*)m_destinationBitmapRightInfoHeader, // The bitmap's header info e.g. width, height, number of bits etc
		DIB_RGB_COLORS,								// Use default 24-bit color table
		SRCCOPY										// Just display
	);
 
	SetStretchBltMode(pDC->GetSafeHdc(),nOldMode);

	ReleaseDC(pDC);


	// Note: 04/24/02 - Added the following:
	// Christopher Wagner cwagner@fas.harvard.edu noticed that memory wasn't being freed

	// Recall OnPortalNotificationProcessedview, which gets called everytime
	// a frame of data arrives, performs 3 steps:
	// (1) grayScaleTheFrameData - which mallocs m_destinationBmp
	// (2) doMyImageProcesing
	// (3) displayMyResults - which we're in now
	// Since we're finished with the memory we malloc'ed for m_destinationBmp
	// we should free it: 
	
	free(m_destinationBmpRight);

	// End of adds


}


void CTripodDlg::grayScaleTheFrameData(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, LPBYTE lpThisBitmapPixelData)
{
	
	// grayScaleTheFrameData: Called by CTripodDlg::OnPortalNotificationBinaryview
	// Task: Read current frame pixel data and computes a grayscale version

	int	W, H;			  // Width and Height of current frame [pixels]
	BYTE            *sourceBmp;		  // Pointer to current frame of data
	int    row, col;
	unsigned long   i;
	BYTE			grayValue;

	BYTE			redValue;
	BYTE			greenValue;
	BYTE			blueValue;

    W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows

	// Store pixel data in row-column vector format
	// Recall that each pixel requires 3 bytes (red, blue and green bytes)
	// m_destinationBmp is a protected member and declared in binarizeDlg.h

	m_destinationBmp = (BYTE*)malloc(H*3*W*sizeof(BYTE));

	// Point to the current frame's pixel data
	sourceBmp = lpThisBitmapPixelData;

	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {

			// Recall each pixel is composed of 3 bytes
			i = (unsigned long)(row*3*W + 3*col);
        
			// The source pixel has a blue, green andred value:
			blueValue  = *(sourceBmp + i);
			greenValue = *(sourceBmp + i + 1);
			redValue   = *(sourceBmp + i + 2);

			// A standard equation for computing a grayscale value based on RGB values
			grayValue = (BYTE)(0.299*redValue + 0.587*greenValue + 0.114*blueValue);

			// The destination BMP will be a grayscale version of the source BMP
			*(m_destinationBmp + i)     = grayValue;
			*(m_destinationBmp + i + 1) = grayValue;
			*(m_destinationBmp + i + 2) = grayValue;
			
		}
	}
}


void CTripodDlg::grayScaleTheFrameDataRight(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, LPBYTE lpThisBitmapPixelData)
{
	
	// grayScaleTheFrameData: Called by CTripodDlg::OnPortalNotificationBinaryview
	// Task: Read current frame pixel data and computes a grayscale version

	int	W, H;			  // Width and Height of current frame [pixels]
	BYTE            *sourceBmp;		  // Pointer to current frame of data
	int    row, col;
	unsigned long   i;
	BYTE			grayValue;

	BYTE			redValue;
	BYTE			greenValue;
	BYTE			blueValue;

    W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
    H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows

	// Store pixel data in row-column vector format
	// Recall that each pixel requires 3 bytes (red, blue and green bytes)
	// m_destinationBmp is a protected member and declared in binarizeDlg.h

	m_destinationBmpRight = (BYTE*)malloc(H*3*W*sizeof(BYTE));

	// Point to the current frame's pixel data
	sourceBmp = lpThisBitmapPixelData;

	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {

			// Recall each pixel is composed of 3 bytes
			i = (unsigned long)(row*3*W + 3*col);
        
			// The source pixel has a blue, green andred value:
			blueValue  = *(sourceBmp + i);
			greenValue = *(sourceBmp + i + 1);
			redValue   = *(sourceBmp + i + 2);

			// A standard equation for computing a grayscale value based on RGB values
			grayValue = (BYTE)(0.299*redValue + 0.587*greenValue + 0.114*blueValue);

			// The destination BMP will be a grayscale version of the source BMP
			*(m_destinationBmpRight + i)     = grayValue;
			*(m_destinationBmpRight + i + 1) = grayValue;
			*(m_destinationBmpRight + i + 2) = grayValue;
			
		}
	}
}

void CTripodDlg::doMyImageProcessing(LPBITMAPINFOHEADER lpThisBitmapInfoHeader, int side)
{
	unsigned int	W, H;			// Width and Height of current frame [pixels]
	W = lpThisBitmapInfoHeader->biWidth;  // biWidth: number of columns
	H = lpThisBitmapInfoHeader->biHeight; // biHeight: number of rows
	unsigned int    row, col;		// Pixel's row and col positions
	unsigned long   i;				// Dummy variable for row-column vector
	int	    upperThreshold = 80;	// Gradient strength nessicary to start edge
	int		lowerThreshold = 40;	// Minimum gradient strength to continue edge
	unsigned long iOffset;			// Variable to offset row-column vector during sobel mask
	int rowOffset;					// Row offset from the current pixel
	int colOffset;					// Col offset from the current pixel
	int rowTotal = 0;				// Row position of offset pixel
	int colTotal = 0;				// Col position of offset pixel
	int Gx;							// Sum of Sobel mask products values in the x direction
	int Gy;							// Sum of Sobel mask products values in the y direction
	double thisAngle;				// Gradient direction based on Gx and Gy
	int newAngle;					// Approximation of the gradient direction
	bool edgeEnd;					// Stores whether or not the edge is at the edge of the possible image
	int GxMask[3][3];				// Sobel mask in the x direction
	int GyMask[3][3];				// Sobel mask in the y direction
	long widthSum = 0;
	long heightSum = 0;
	long pixelTotal = 0;
	int newPixel;					// Sum pixel values for gaussian
	int gaussianMask[5][5];			// Gaussian mask


	char			str[80];		  // To print message 
	CDC				*pDC;			  // Device context need to print message

	/* Declare Sobel masks */
	GxMask[0][0] = -1; GxMask[0][1] = 0; GxMask[0][2] = 1;
	GxMask[1][0] = -2; GxMask[1][1] = 0; GxMask[1][2] = 2;
	GxMask[2][0] = -1; GxMask[2][1] = 0; GxMask[2][2] = 1;
	
	GyMask[0][0] =  1; GyMask[0][1] =  2; GyMask[0][2] =  1;
	GyMask[1][0] =  0; GyMask[1][1] =  0; GyMask[1][2] =  0;
	GyMask[2][0] = -1; GyMask[2][1] = -2; GyMask[2][2] = -1;

	/* Declare Gaussian mask */
	gaussianMask[0][0] = 2;		gaussianMask[0][1] = 4;		gaussianMask[0][2] = 5;		gaussianMask[0][3] = 4;		gaussianMask[0][4] = 2;	
	gaussianMask[1][0] = 4;		gaussianMask[1][1] = 9;		gaussianMask[1][2] = 12;	gaussianMask[1][3] = 9;		gaussianMask[1][4] = 4;	
	gaussianMask[2][0] = 5;		gaussianMask[2][1] = 12;	gaussianMask[2][2] = 15;	gaussianMask[2][3] = 12;	gaussianMask[2][4] = 2;	
	gaussianMask[3][0] = 4;		gaussianMask[3][1] = 9;		gaussianMask[3][2] = 12;	gaussianMask[3][3] = 9;		gaussianMask[3][4] = 4;	
	gaussianMask[4][0] = 2;		gaussianMask[4][1] = 4;		gaussianMask[4][2] = 5;		gaussianMask[4][3] = 4;		gaussianMask[4][4] = 2;	
	
	long cornerMaskVal[8];
	long max;
	int orientation;
	int curPixel;
	long cornerThreshold = 70000;
	int halfMask = 8;
	int count;

	double shift;
	double distance = 0.0;

	for (row = 0; row < H; row++) {
		for (col = 0; col < W; col++) {
			edgeDir[row][col] = 0;			
		}
	}

	switch (side) {
	case 4:

		/* Gaussian Blur */
		for (row = 2; row < H-2; row++) {
			for (col = 2; col < W-2; col++) {
				newPixel = 0;
				for (rowOffset=-2; rowOffset<=2; rowOffset++) {
					for (colOffset=-2; colOffset<=2; colOffset++) {
						rowTotal = row + rowOffset;
						colTotal = col + colOffset;
						iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
						newPixel += (*(m_destinationBmpRight + iOffset)) * gaussianMask[2 + rowOffset][2 + colOffset];
					}
				}
				i = (unsigned long)(row*3*W + col*3);
				*(m_destinationBmpRight + i) = newPixel / 159;
			}
		}

		/* Determine edge directions and gradient strengths */
		for (row = 1; row < H-1; row++) {
			for (col = 1; col < W-1; col++) {
				i = (unsigned long)(row*3*W + 3*col);
				Gx = 0;
				Gy = 0;
				/* Calculate the sum of the Sobel mask times the nine surrounding pixels in the x and y direction */
				for (rowOffset=-1; rowOffset<=1; rowOffset++) {
					for (colOffset=-1; colOffset<=1; colOffset++) {
						rowTotal = row + rowOffset;
						colTotal = col + colOffset;
						iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
						Gx = Gx + (*(m_destinationBmpRight + iOffset) * GxMask[rowOffset + 1][colOffset + 1]);
						Gy = Gy + (*(m_destinationBmpRight + iOffset) * GyMask[rowOffset + 1][colOffset + 1]);
					}
				}

				gradient[row][col] = sqrt(pow(Gx,2.0) + pow(Gy,2.0));	// Calculate gradient strength			
				thisAngle = (atan2(Gx,Gy)/3.14159) * 180.0;		// Calculate actual direction of edge
				
				/* Convert actual edge direction to approximate value */
				if ( ( (thisAngle < 22.5) && (thisAngle > -22.5) ) || (thisAngle > 157.5) || (thisAngle < -157.5) )
					newAngle = 0;
				if ( ( (thisAngle > 22.5) && (thisAngle < 67.5) ) || ( (thisAngle < -112.5) && (thisAngle > -157.5) ) )
					newAngle = 45;
				if ( ( (thisAngle > 67.5) && (thisAngle < 112.5) ) || ( (thisAngle < -67.5) && (thisAngle > -112.5) ) )
					newAngle = 90;
				if ( ( (thisAngle > 112.5) && (thisAngle < 157.5) ) || ( (thisAngle < -22.5) && (thisAngle > -67.5) ) )
					newAngle = 135;
					
				edgeDir[row][col] = newAngle;		// Store the approximate edge direction of each pixel in one array
			}
		}

		/* Trace along all the edges in the image */
		for (row = 1; row < H - 1; row++) {
			for (col = 1; col < W - 1; col++) {
				edgeEnd = false;
				if (gradient[row][col] > upperThreshold) {		// Check to see if current pixel has a high enough gradient strength to be part of an edge
					/* Switch based on current pixel's edge direction */
					switch (edgeDir[row][col]){		
						case 0:
							findEdge(0, 1, row, col, 0, lowerThreshold, 1);
							break;
						case 45:
							findEdge(1, 1, row, col, 45, lowerThreshold, 1);
							break;
						case 90:
							findEdge(1, 0, row, col, 90, lowerThreshold, 1);
							break;
						case 135:
							findEdge(1, -1, row, col, 135, lowerThreshold, 1);
							break;
						default :
							i = (unsigned long)(row*3*W + 3*col);
							*(m_destinationBmpRight + i) = 
							*(m_destinationBmpRight + i + 1) = 
							*(m_destinationBmpRight + i + 2) = 0;
							break;
						}
					}
				else {
					i = (unsigned long)(row*3*W + 3*col);
						*(m_destinationBmpRight + i) = 
						*(m_destinationBmpRight + i + 1) = 
						*(m_destinationBmpRight + i + 2) = 0;
				}	
			}
		}
		
		/* Suppress any pixels not changed by the edge tracing */
		for (row = 0; row < H; row++) {
			for (col = 0; col < W; col++) {	
				// Recall each pixel is composed of 3 bytes
				i = (unsigned long)(row*3*W + 3*col);
				// If a pixel's grayValue is not black or white make it black
				if( ((*(m_destinationBmpRight + i) != 255) && (*(m_destinationBmpRight + i) != 0)) 
				 || ((*(m_destinationBmpRight + i + 1) != 255) && (*(m_destinationBmpRight + i + 1) != 0)) 
				 || ((*(m_destinationBmpRight + i + 2) != 255) && (*(m_destinationBmpRight + i + 2) != 0)) ) 
					*(m_destinationBmpRight + i) = 
					*(m_destinationBmpRight + i + 1) = 
					*(m_destinationBmpRight + i + 2) = 0; // Make pixel black
			}
		}

		/* Non-maximum Suppression */
		for (row = 1; row < H - 1; row++) {
			for (col = 1; col < W - 1; col++) {
				i = (unsigned long)(row*3*W + 3*col);
				if (*(m_destinationBmp + i) == 255) {		// Check to see if current pixel is an edge
					/* Switch based on current pixel's edge direction */
					switch (edgeDir[row][col]) {		
						case 0:
							suppressNonMax( 1, 0, row, col, 0, lowerThreshold, 0);
							break;
						case 45:
							suppressNonMax( 1, -1, row, col, 45, lowerThreshold, 0);
							break;
						case 90:
							suppressNonMax( 0, 1, row, col, 90, lowerThreshold, 0);
							break;
						case 135:
							suppressNonMax( 1, 1, row, col, 135, lowerThreshold, 0);
							break;
						default :
							break;
					}
				}	
			}
		}

		// X , Y , Corner Strength 
		cornersRight[0][0] = -1;
		cornersRight[1][0] = -1;
		cornersRight[2][0] = -1;
		cornersRight[3][0] = -1;
		cornersRight[0][1] = -1;
		cornersRight[1][1] = -1;
		cornersRight[2][1] = -1;
		cornersRight[3][1] = -1;
		cornersRight[0][2] = 0;
		cornersRight[1][2] = 0;
		cornersRight[2][2] = 0;
		cornersRight[3][2] = 0;

		// Find Corners
		for (row = halfMask + 21; row < H - halfMask - 21; row++) {
			for (col = halfMask + 21; col < W - halfMask - 21; col++) {
				for (count = 0; count < 4; count++) {
					cornerMaskVal[count] = 0;
				}
					for (rowOffset=-halfMask; rowOffset<=halfMask; rowOffset++) {
						for (colOffset=-halfMask; colOffset<=halfMask; colOffset++) {
							rowTotal = row + rowOffset;
							colTotal = col + colOffset;
							iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
							curPixel = *(m_destinationBmpRight + iOffset);
							for (count = 0; count < 4; count++) {
								cornerMaskVal[count] += (cornerMask[count][rowOffset + halfMask][colOffset + halfMask] * curPixel);
							}
						}
					}
				
				orientation = 10;
				max = 0;
				
				for (count = 0; count < 4; count++) {
					if((cornerMaskVal[count] > max) && (cornerMaskVal[count] > cornerThreshold)) {
						max = cornerMaskVal[count];
						orientation = count;
					}
				}
				
				if	((orientation != 10) && (max > cornersRight[orientation][2])) {
					cornersRight[orientation][0] = col;
					cornersRight[orientation][1] = row;
					cornersRight[orientation][2] = max;
				}
					
			}
		}


		// Mark Corners
		for (count = 0; count < 4; count++) {
			if(cornersRight[count][2] != 0) {
				for (rowOffset=-halfMask; rowOffset<=halfMask; rowOffset++) {
					rowTotal = cornersRight[count][1] + rowOffset;
					if( (rowTotal < (H - halfMask - 1)) && (rowTotal > halfMask) ) {
						iOffset = (unsigned long)(rowTotal*3*W + cornersRight[count][0]*3);
						if(cornerMask[count][rowOffset + halfMask][8] == center) {
							*(m_destinationBmpRight + iOffset + 1) = 255;
							*(m_destinationBmpRight + iOffset + 2) = 0;
						} else {	
							*(m_destinationBmpRight + iOffset + 1) = 0;
							*(m_destinationBmpRight + iOffset + 2) = 255;
						}
					}
				}

				for (colOffset=-halfMask; colOffset<=halfMask; colOffset++) {
					colTotal = cornersRight[count][0] + colOffset;
					if( (colTotal < (W - halfMask - 1)) && (colTotal > halfMask) ) {
						iOffset = (unsigned long)(cornersRight[count][1]*3*W + colTotal*3);
						if(cornerMask[count][8][colOffset + halfMask] == center) {
							*(m_destinationBmpRight + iOffset + 1) = 255;
							*(m_destinationBmpRight + iOffset + 2) = 0;
						} else {
							*(m_destinationBmpRight + iOffset + 1) = 0;
							*(m_destinationBmpRight + iOffset + 2) = 255;
						}
					}
				}
			}
		}

		pDC = GetDC();
		sprintf(str, "Top Left: %d,%d    Top Right: %d,%d   ", cornersRight[2][0], cornersRight[2][1], cornersRight[3][0], cornersRight[3][1]);
		pDC->TextOut(360, 620, str);
		ReleaseDC(pDC);

		pDC = GetDC();
		sprintf(str, "Bottom Left: %d,%d    Bottom Right: %d,%d   ", cornersRight[0][0], cornersRight[0][1], cornersRight[1][0], cornersRight[1][1]);
		pDC->TextOut(360, 640, str);
		ReleaseDC(pDC);
		break;


	case 5:

		/* Gaussian Blur */
		for (row = 2; row < H-2; row++) {
			for (col = 2; col < W-2; col++) {
				newPixel = 0;
				for (rowOffset=-2; rowOffset<=2; rowOffset++) {
					for (colOffset=-2; colOffset<=2; colOffset++) {
						rowTotal = row + rowOffset;
						colTotal = col + colOffset;
						iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
						newPixel += (*(m_destinationBmp + iOffset)) * gaussianMask[2 + rowOffset][2 + colOffset];
					}
				}
				i = (unsigned long)(row*3*W + col*3);
				*(m_destinationBmp + i) = newPixel / 159;
			}
		}

		/* Determine edge directions and gradient strengths */
		for (row = 1; row < H-1; row++) {
			for (col = 1; col < W-1; col++) {
				i = (unsigned long)(row*3*W + 3*col);
				Gx = 0;
				Gy = 0;
				/* Calculate the sum of the Sobel mask times the nine surrounding pixels in the x and y direction */
				for (rowOffset=-1; rowOffset<=1; rowOffset++) {
					for (colOffset=-1; colOffset<=1; colOffset++) {
						rowTotal = row + rowOffset;
						colTotal = col + colOffset;
						iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
						Gx = Gx + (*(m_destinationBmp + iOffset) * GxMask[rowOffset + 1][colOffset + 1]);
						Gy = Gy + (*(m_destinationBmp + iOffset) * GyMask[rowOffset + 1][colOffset + 1]);
					}
				}

				gradient[row][col] = sqrt(pow(Gx,2.0) + pow(Gy,2.0));	// Calculate gradient strength			
				thisAngle = (atan2(Gx,Gy)/3.14159) * 180.0;		// Calculate actual direction of edge
				
				/* Convert actual edge direction to approximate value */
				if ( ( (thisAngle < 22.5) && (thisAngle > -22.5) ) || (thisAngle > 157.5) || (thisAngle < -157.5) )
					newAngle = 0;
				if ( ( (thisAngle > 22.5) && (thisAngle < 67.5) ) || ( (thisAngle < -112.5) && (thisAngle > -157.5) ) )
					newAngle = 45;
				if ( ( (thisAngle > 67.5) && (thisAngle < 112.5) ) || ( (thisAngle < -67.5) && (thisAngle > -112.5) ) )
					newAngle = 90;
				if ( ( (thisAngle > 112.5) && (thisAngle < 157.5) ) || ( (thisAngle < -22.5) && (thisAngle > -67.5) ) )
					newAngle = 135;
					
				edgeDir[row][col] = newAngle;		// Store the approximate edge direction of each pixel in one array
			}
		}

		/* Trace along all the edges in the image */
		for (row = 1; row < H - 1; row++) {
			for (col = 1; col < W - 1; col++) {
				edgeEnd = false;
				if (gradient[row][col] > upperThreshold) {		// Check to see if current pixel has a high enough gradient strength to be part of an edge
					/* Switch based on current pixel's edge direction */
					switch (edgeDir[row][col]){		
						case 0:
							findEdge(0, 1, row, col, 0, lowerThreshold, 0);
							break;
						case 45:
							findEdge(1, 1, row, col, 45, lowerThreshold, 0);
							break;
						case 90:
							findEdge(1, 0, row, col, 90, lowerThreshold, 0);
							break;
						case 135:
							findEdge(1, -1, row, col, 135, lowerThreshold, 0);
							break;
						default :
							i = (unsigned long)(row*3*W + 3*col);
							*(m_destinationBmp + i) = 
							*(m_destinationBmp + i + 1) = 
							*(m_destinationBmp + i + 2) = 0;
							break;
						}
					}
				else {
					i = (unsigned long)(row*3*W + 3*col);
						*(m_destinationBmp + i) = 
						*(m_destinationBmp + i + 1) = 
						*(m_destinationBmp + i + 2) = 0;
				}	
			}
		}
		
		/* Suppress any pixels not changed by the edge tracing */
		for (row = 0; row < H; row++) {
			for (col = 0; col < W; col++) {	
				// Recall each pixel is composed of 3 bytes
				i = (unsigned long)(row*3*W + 3*col);
				// If a pixel's grayValue is not black or white make it black
				if( ((*(m_destinationBmp + i) != 255) && (*(m_destinationBmp + i) != 0)) 
				 || ((*(m_destinationBmp + i + 1) != 255) && (*(m_destinationBmp + i + 1) != 0)) 
				 || ((*(m_destinationBmp + i + 2) != 255) && (*(m_destinationBmp + i + 2) != 0)) ) 
					*(m_destinationBmp + i) = 
					*(m_destinationBmp + i + 1) = 
					*(m_destinationBmp + i + 2) = 0; // Make pixel black
			}
		}

		/* Non-maximum Suppression */
		for (row = 1; row < H - 1; row++) {
			for (col = 1; col < W - 1; col++) {
				i = (unsigned long)(row*3*W + 3*col);
				if (*(m_destinationBmp + i) == 255) {		// Check to see if current pixel is an edge
					/* Switch based on current pixel's edge direction */
					switch (edgeDir[row][col]) {		
						case 0:
							suppressNonMax( 1, 0, row, col, 0, lowerThreshold, 0);
							break;
						case 45:
							suppressNonMax( 1, -1, row, col, 45, lowerThreshold, 0);
							break;
						case 90:
							suppressNonMax( 0, 1, row, col, 90, lowerThreshold, 0);
							break;
						case 135:
							suppressNonMax( 1, 1, row, col, 135, lowerThreshold, 0);
							break;
						default :
							break;
					}
				}	
			}
		}


		// X , Y , Corner Strength 
		cornersLeft[0][0] = -1;
		cornersLeft[1][0] = -1;
		cornersLeft[2][0] = -1;
		cornersLeft[3][0] = -1;
		cornersLeft[0][1] = -1;
		cornersLeft[1][1] = -1;
		cornersLeft[2][1] = -1;
		cornersLeft[3][1] = -1;
		cornersLeft[0][2] = 0;
		cornersLeft[1][2] = 0;
		cornersLeft[2][2] = 0;
		cornersLeft[3][2] = 0;
		
		// Find Corners
		for (row = halfMask + 21; row < H - halfMask - 21; row++) {
			for (col = halfMask + 21; col < W - halfMask - 21; col++) {
				for (count = 0; count < 8; count++) {
					cornerMaskVal[count] = 0;
				}
					for (rowOffset=-halfMask; rowOffset<=halfMask; rowOffset++) {
						for (colOffset=-halfMask; colOffset<=halfMask; colOffset++) {
							rowTotal = row + rowOffset;
							colTotal = col + colOffset;
							iOffset = (unsigned long)(rowTotal*3*W + colTotal*3);
							curPixel = *(m_destinationBmp + iOffset);
							for (count = 0; count < 8; count++) {
								cornerMaskVal[count] += (cornerMask[count][rowOffset + halfMask][colOffset + halfMask] * curPixel);
							}
						}
					}
				
				orientation = 10;
				max = 0;
				
				for (count = 0; count < 4; count++) {
					if((cornerMaskVal[count] > max) && (cornerMaskVal[count] > 70000)) {
						max = cornerMaskVal[count];
						orientation = count;
					}
				}
				
				// Limit to square corners
				if	((orientation != 10) && (max > cornersLeft[orientation][2])) {
					cornersLeft[orientation][0] = col;
					cornersLeft[orientation][1] = row;
					cornersLeft[orientation][2] = max;
				}
					
			}
		}
		
		// Mark Corners
		for (count = 0; count < 4; count++) {
			if(cornersLeft[count][2] != 0) {
			for (rowOffset=-halfMask; rowOffset<=halfMask; rowOffset++) {
				rowTotal = cornersLeft[count][1] + rowOffset;
				iOffset = (unsigned long)(rowTotal*3*W + cornersLeft[count][0]*3);
				if(cornerMask[count][rowOffset + halfMask][8] == center) {
					*(m_destinationBmp + iOffset + 1) = 255;
					*(m_destinationBmp + iOffset + 2) = 0;
				} else {	
					*(m_destinationBmp + iOffset + 1) = 0;
					*(m_destinationBmp + iOffset + 2) = 255;
				}
			}

			for (colOffset=-halfMask; colOffset<=halfMask; colOffset++) {	
				colTotal = cornersLeft[count][0] + colOffset;
				iOffset = (unsigned long)(cornersLeft[count][1]*3*W + colTotal*3);
				if(cornerMask[count][8][colOffset + halfMask] == center) {
					*(m_destinationBmp + iOffset + 1) = 255;
					*(m_destinationBmp + iOffset + 2) = 0;
				} else {
					*(m_destinationBmp + iOffset + 1) = 0;
					*(m_destinationBmp + iOffset + 2) = 255;
				}
			}
			}
		}


		
		if ( (cornersRight[0][2] != 0) && (cornersRight[2][2] != 0) && (cornersLeft[0][2] != 0) && (cornersLeft[2][2] != 0) ) {
			if ( ( abs(cornersRight[0][0] - cornersRight[2][0]) < 6 ) && ( abs(cornersLeft[0][0] - cornersLeft[2][0]) < 6 ) ) {
				shift = abs((cornersRight[0][0] + cornersRight[2][0])/2 - (cornersLeft[0][0] + cornersLeft[2][0])/2);
				distance = 2946.5 * pow(shift, -1.0259);
				//distance = (914.0 * 2.9) / shift;
			}
		} 
		else if ( (cornersRight[1][2] != 0) && (cornersRight[3][2] != 0) && (cornersLeft[1][2] != 0) && (cornersLeft[3][2] != 0) ) {
			if ( ( abs(cornersRight[1][0] - cornersRight[3][0]) < 6 ) && ( abs(cornersLeft[1][0] - cornersLeft[3][0]) < 6 ) ) {
				shift = abs((cornersRight[1][0] + cornersRight[3][0])/2 - (cornersLeft[1][0] + cornersLeft[3][0]) / 2);
				distance = 2946.5 * pow(shift, -1.0259);
				//distance = (914.0 * 2.9) / shift;
			}
		} 

		pDC = GetDC();
		sprintf(str, "Distance:  %f  Shift:  %f                   ", distance, shift);
		pDC->TextOut(15, 640, str);
		ReleaseDC(pDC);

		pDC = GetDC();
		sprintf(str, "Top Left: %d,%d   Top Right: %d,%d   ", cornersLeft[2][0], cornersLeft[2][1], cornersLeft[3][0], cornersLeft[3][1]);
		pDC->TextOut(360, 580, str);
		ReleaseDC(pDC);

		pDC = GetDC();
		sprintf(str, "Bottom Left: %d,%d   Bottom Right: %d,%d   ", cornersLeft[0][0], cornersLeft[0][1], cornersLeft[1][0], cornersLeft[1][1]);
		pDC->TextOut(360, 600, str);
		ReleaseDC(pDC);
		break;
	}


}


void CTripodDlg::OnTimer(UINT nIDEvent) 
{
	if(countStart < 4)
		countStart++;

	if ((whichSide) && (wasDisplayed)) {

		// Change Camera
		m_cVideoUnprocessedView.ConnectCamera(1);

		whichSide = false;
		wasDisplayed = false;

		grayScaleTheFrameData(lpBitmapInfoHeader, lpBitmapPixelData);
		doMyImageProcessing(lpBitmapInfoHeader, countStart + 1); // Place where you'd add your image processing code
		displayMyResults(lpBitmapInfoHeader);
	}
	else if(wasDisplayed) {

		// Change Camera
		m_cVideoUnprocessedView.ConnectCamera(0);

		whichSide = true;
		wasDisplayed = false;

		grayScaleTheFrameDataRight(lpBitmapInfoHeader, lpBitmapPixelData);
		doMyImageProcessing(lpBitmapInfoHeader, countStart); // Place where you'd add your image processing code
		displayMyResultsRight(lpBitmapInfoHeader);
	}


	CDialog::OnTimer(nIDEvent);
}

void CTripodDlg::findEdge(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold, int side)
{
	int W = 320;
	int H = 240;
	int newRow;
	int newCol;
	unsigned long i;
	bool edgeEnd = false;

	/* Find the row and column values for the next possible pixel on the edge */
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
		
	/* Determine edge directions and gradient strengths */
	while ( (edgeDir[newRow][newCol]==dir) && !edgeEnd && (gradient[newRow][newCol] > lowerThreshold) ) {
		/* Set the new pixel as white to show it is an edge */
		i = (unsigned long)(newRow*3*W + 3*newCol);
		if (side = 0) {
			*(m_destinationBmp + i) =
			*(m_destinationBmp + i + 1) =
			*(m_destinationBmp + i + 2) = 255;
		} else if (side = 1) {
			*(m_destinationBmpRight + i) =
			*(m_destinationBmpRight + i + 1) =
			*(m_destinationBmpRight + i + 2) = 255;
		}
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
	}	
}

void CTripodDlg::suppressNonMax(int rowShift, int colShift, int row, int col, int dir, int lowerThreshold, int side)
{
	int W = 320;
	int H = 240;
	int newRow = 0;
	int newCol = 0;
	unsigned long i;
	bool edgeEnd = false;
	float nonMax[320][3];			// Temporarily stores gradients and positions of pixels in parallel edges
	int pixelCount = 0;					// Stores the number of pixels in parallel edges
	int count;						// A for loop counter
	int max[3];						// Maximum point in a wide edge
	
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;		// If the next pixel would be off image, don't do the while loop
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	/* Find non-maximum parallel edges tracing up */
	while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
		nonMax[pixelCount][0] = newRow;
		nonMax[pixelCount][1] = newCol;
		nonMax[pixelCount][2] = gradient[newRow][newCol];
		pixelCount++;
		i = (unsigned long)(newRow*3*W + 3*newCol);
	}

	/* Find non-maximum parallel edges tracing down */
	edgeEnd = false;
	colShift *= -1;
	rowShift *= -1;
	if (colShift < 0) {
		if (col > 0)
			newCol = col + colShift;
		else
			edgeEnd = true;
	} else if (col < W - 1) {
		newCol = col + colShift;
	} else
		edgeEnd = true;	
	if (rowShift < 0) {
		if (row > 0)
			newRow = row + rowShift;
		else
			edgeEnd = true;
	} else if (row < H - 1) {
		newRow = row + rowShift;
	} else
		edgeEnd = true;	
	i = (unsigned long)(newRow*3*W + 3*newCol);
	while ((edgeDir[newRow][newCol] == dir) && !edgeEnd && (*(m_destinationBmp + i) == 255)) {
		if (colShift < 0) {
			if (newCol > 0)
				newCol = newCol + colShift;
			else
				edgeEnd = true;	
		} else if (newCol < W - 1) {
			newCol = newCol + colShift;
		} else
			edgeEnd = true;	
		if (rowShift < 0) {
			if (newRow > 0)
				newRow = newRow + rowShift;
			else
				edgeEnd = true;
		} else if (newRow < H - 1) {
			newRow = newRow + rowShift;
		} else
			edgeEnd = true;	
		nonMax[pixelCount][0] = newRow;
		nonMax[pixelCount][1] = newCol;
		nonMax[pixelCount][2] = gradient[newRow][newCol];
		pixelCount++;
		i = (unsigned long)(newRow*3*W + 3*newCol);
	}

	/* Suppress non-maximum edges */
	max[0] = 0;
	max[1] = 0;
	max[2] = 0;
	for (count = 0; count < pixelCount; count++) {
		if (nonMax[count][2] > max[2]) {
			max[0] = nonMax[count][0];
			max[1] = nonMax[count][1];
			max[2] = nonMax[count][2];
		}
	}
	if (side = 0) {
		for (count = 0; count < pixelCount; count++) {
			i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
			*(m_destinationBmp + i) = 
			*(m_destinationBmp + i + 1) = 
			*(m_destinationBmp + i + 2) = 0;
		}
	} else if (side = 1) {
		for (count = 0; count < pixelCount; count++) {
			i = (unsigned long)(nonMax[count][0]*3*W + 3*nonMax[count][1]);
			*(m_destinationBmpRight + i) = 
			*(m_destinationBmpRight + i + 1) = 
			*(m_destinationBmpRight + i + 2) = 0;
		}
	}
}


/* Modified Bill Green Bitmap Code */

/*-------PROTOTYPES-------*/
long getImageInfo(FILE*, long, int);

/*------STRUCTURES--------*/
typedef struct {int rows; int cols; unsigned char* data;} sImage;

void CTripodDlg::getMaskBmp(int fileInt)
{
	FILE			*bmpInput;
	sImage		originalImage;
	unsigned char		someChar;
	unsigned char		*pChar;
	long			fileSize;
	int			nColors;
	int			r, c;
	int			redValue, greenValue, blueValue;
	/*--------INITIALIZE POINTER----------*/
	someChar = '0';
	pChar = &someChar;

	/*----DECLARE INPUT AND OUTPUT FILES----*/
	switch (fileInt){
	case 0:
		bmpInput = fopen("corner1Mask.bmp", "rb");
	break;
	case 1:
		bmpInput = fopen("corner2Mask.bmp", "rb");
	break;
	case 2:
		bmpInput = fopen("corner3Mask.bmp", "rb");
	break;
	case 3:
		bmpInput = fopen("corner4Mask.bmp", "rb");
	break;
	}

	fseek(bmpInput, 0L, SEEK_END);

	/*-----GET BMP DATA-----*/
	originalImage.cols = (int)getImageInfo(bmpInput, 18, 4);
	originalImage.rows = (int)getImageInfo(bmpInput, 22, 4);
	fileSize = getImageInfo(bmpInput, 2, 4);
	nColors = getImageInfo(bmpInput, 46, 4);

	/*----FOR 24-BIT BMP, THERE IS NO COLOR TABLE-----*/
	fseek(bmpInput, 54, SEEK_SET);

	/*-----------READ RASTER DATA-----------*/
	for(r=originalImage.rows-1; r>=0; r--) {
		for(c=0; c<originalImage.cols; c++) {
			/*----READ FIRST BYTE TO GET BLUE VALUE-----*/
			fread(pChar, sizeof(char), 1, bmpInput);
			blueValue = *pChar;

			/*-----READ NEXT BYTE TO GET GREEN VALUE-----*/
			fread(pChar, sizeof(char), 1, bmpInput);
			greenValue = *pChar;

			/*-----READ NEXT BYTE TO GET RED VALUE-----*/
			fread(pChar, sizeof(char), 1, bmpInput);
			redValue = *pChar;

			if(blueValue == 255)
				cornerMask[fileInt][r][c] = 24;
			else if(blueValue == 0) 
				cornerMask[fileInt][r][c] = -4;
			else if(blueValue == 128)
				cornerMask[fileInt][r][c] = 4;
			else if(blueValue == 64)
				cornerMask[fileInt][r][c] = 1;
			else
				cornerMask[fileInt][r][c] = 0;
		}
		fread(pChar, sizeof(char), 1, bmpInput);
	}
	fclose(bmpInput);
}

/*----GET IMAGE INFO SUBPROGRAM------*/

long CTripodDlg::getImageInfo(FILE* inputFile, long offset, int numberOfChars)
{
	unsigned char		*ptrC;
	long			value=0L;
	int			i;
	unsigned char		dummy;

	dummy = '0';
	ptrC = &dummy;

	fseek(inputFile, offset, SEEK_SET);

	for(i=1; i<=numberOfChars; i++)
	{
		fread(ptrC, sizeof(char), 1, inputFile);
		/* calculate value based on adding bytes */
		value = (long)(value + (*ptrC)*(pow(256, (i-1))));
	}

	return(value);
}

/* End Modified Bill Green Bitmap Code */
