// HoughTransform.h: interface for the CHoughTransform class.
//
//////////////////////////////////////////////////////////////////////

#ifndef __HOUGHTRANSFORM_H__
#define __HOUGHTRANSFORM_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <vector>
using namespace std;

#include "PreprocFuncs.h"
#include "stdlib.h"

#define SINCOSFIXEBIT 12

#define RESMASK (0xFFFFFFFF >> (32 - SINCOSFIXEBIT))

#define CURVATURE_COEFS_SHIFT_1 16
#define CURVATURE_COEFS_SHIFT_2 20
#define CURVATURE_COEFS_SHIFT_3 30
#define XY_IN_SHIFT 4
#define CURVATURE_RES_SHIFT 16


#define CURVATURE_COEF_RES_SHIFT_1 (CURVATURE_COEFS_SHIFT_1 - CURVATURE_RES_SHIFT)
#define CURVATURE_COEF_RES_SHIFT_2 (CURVATURE_COEFS_SHIFT_2 - CURVATURE_RES_SHIFT)
#define CURVATURE_COEF_RES_SHIFT_3 (CURVATURE_COEFS_SHIFT_3 - CURVATURE_RES_SHIFT)

inline LONGLONG IdentityFunc(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return (nY << CURVATURE_RES_SHIFT);
}

inline LONGLONG CalcYCorrection_1_1(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return pCoefs[0] 
	+ ((pCoefs[1] * nX + pCoefs[2] * nY) >> CURVATURE_COEF_RES_SHIFT_1);
}

inline LONGLONG CalcYCorrection_2_1(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return CalcYCorrection_1_1(nX, nY, pCoefs)
		+ ((pCoefs[3] * nX * nX) >> CURVATURE_COEF_RES_SHIFT_2)
		+ ((pCoefs[4] * nX * nY) >> CURVATURE_COEF_RES_SHIFT_2);
}

inline LONGLONG CalcYCorrection_2_2(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return CalcYCorrection_2_1(nX, nY, pCoefs)
		+ ((pCoefs[5] * nY * nY) >> CURVATURE_COEF_RES_SHIFT_2);
}

inline LONGLONG CalcYCorrection_3_1(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	LONGLONG nXSQR = nX * nX;
	return CalcYCorrection_2_1(nX, nY, pCoefs)
		+ ((pCoefs[5] * nX * nXSQR) >> CURVATURE_COEF_RES_SHIFT_3)
		+ ((pCoefs[6] * nY * nXSQR) >> CURVATURE_COEF_RES_SHIFT_3);
}

inline LONGLONG CalcYCorrection_3_2(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	LONGLONG nXSQR = nX * nX;
	return CalcYCorrection_2_2(nX, nY, pCoefs)
		+ ((pCoefs[6] * nX * nXSQR) >> CURVATURE_COEF_RES_SHIFT_3)
		+ ((pCoefs[7] * nY * nXSQR) >> CURVATURE_COEF_RES_SHIFT_3)
		+ ((pCoefs[8] * nX * nY * nY) >> CURVATURE_COEF_RES_SHIFT_3);
}

inline LONGLONG CalcYCorrection_3_3(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return CalcYCorrection_3_2(nX, nY, pCoefs)
		+ ((pCoefs[9] * nY * nY * nY) >> CURVATURE_COEF_RES_SHIFT_3);
}

////////////////////////////////WithScale///////////////////////////////////
inline LONGLONG CalcYCorrectionScale_1_1(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return pCoefs[0] 
	+ ((pCoefs[1] * nX + pCoefs[2] * nY) >> (CURVATURE_COEF_RES_SHIFT_1 + XY_IN_SHIFT));
}

inline LONGLONG CalcYCorrectionScale_2_1(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return CalcYCorrectionScale_1_1(nX, nY, pCoefs)
		+ ((pCoefs[3] * nX * nX) >> (CURVATURE_COEF_RES_SHIFT_2 + XY_IN_SHIFT * 2))
		+ ((pCoefs[4] * nX * nY) >> (CURVATURE_COEF_RES_SHIFT_2 + XY_IN_SHIFT * 2));
}

inline LONGLONG CalcYCorrectionScale_2_2(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return CalcYCorrectionScale_2_1(nX, nY, pCoefs)
		+ ((pCoefs[5] * nY * nY) >> (CURVATURE_COEF_RES_SHIFT_2 + XY_IN_SHIFT * 2));
}

inline LONGLONG CalcYCorrectionScale_3_1(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	LONGLONG nXSQR = (nX * nX) >>  (XY_IN_SHIFT * 2);
	return CalcYCorrectionScale_2_1(nX, nY, pCoefs)
		+ ((pCoefs[5] * nX * nXSQR) >> (CURVATURE_COEF_RES_SHIFT_3 + XY_IN_SHIFT))
		+ ((pCoefs[6] * nY * nXSQR) >> (CURVATURE_COEF_RES_SHIFT_3 + XY_IN_SHIFT));
}

inline LONGLONG CalcYCorrectionScale_3_2(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	LONGLONG nXSQR = (nX * nX) >>  (XY_IN_SHIFT * 2);
	return CalcYCorrectionScale_2_2(nX, nY, pCoefs)
		+ ((pCoefs[6] * nX * nXSQR) >> (CURVATURE_COEF_RES_SHIFT_3 + XY_IN_SHIFT))
		+ ((pCoefs[7] * nY * nXSQR) >> (CURVATURE_COEF_RES_SHIFT_3 + XY_IN_SHIFT))
		+ ((pCoefs[8] * nX * nY * nY) >> (CURVATURE_COEF_RES_SHIFT_3 + XY_IN_SHIFT * 3));
}

inline LONGLONG CalcYCorrectionScale_3_3(LONGLONG nX, LONGLONG nY, const LONGLONG *pCoefs){
	return CalcYCorrectionScale_3_2(nX, nY, pCoefs)
		+ ((pCoefs[9] * nY * nY * nY) >> (CURVATURE_COEF_RES_SHIFT_3 + XY_IN_SHIFT * 3));
}

inline UCHAR GetImageByteBL(int x, int y, const UCHAR *pSrc, int nWidth, int nHeight){
	UINT nX = x >> SINCOSFIXEBIT;
	UINT nY = y >> SINCOSFIXEBIT;

	UINT dx = x & RESMASK;
	UINT dy = y & RESMASK;

	UINT nIndx = nY * nWidth + nX;
	UINT v0 = pSrc[nIndx];
	UINT v2 = pSrc[nIndx + nWidth];

	UINT v1 = pSrc[nIndx+1];
	UINT v3 = pSrc[nIndx + nWidth + 1];
	UINT vx1 = v0 * ((1<<SINCOSFIXEBIT) - dx) + dx * v1;
	UINT vx2 = v2 * ((1<<SINCOSFIXEBIT) - dx) + dx * v3;
	UINT vy = vx1 * ((1<<SINCOSFIXEBIT) - dy) + dy * vx2;
	return (UCHAR)((vy +(1<<(SINCOSFIXEBIT*2 -1)))>> (SINCOSFIXEBIT*2));
}

inline UCHAR GetImageByteNN(int x, int y, const UCHAR *pSrc, int nWidth, int nHeight){
	UINT nX = x >> SINCOSFIXEBIT;
	UINT nY = y >> SINCOSFIXEBIT;

	UINT dx = x & RESMASK;
	UINT dy = y & RESMASK;
	if(dx >= (RESMASK >> 1)) ++nX;
	if(dy >= (RESMASK >> 1)) ++nY;

	return pSrc[nY * nWidth + nX];
}

typedef LONGLONG (*GET_VALUE_FUNCTION)(LONGLONG, LONGLONG, const LONGLONG *);

GET_VALUE_FUNCTION GetYCorrectionFunction(int nOrderX, int nOrderY);

class ImgRotationInfo{
public:
	CPoint origCenter, rotCenter; 
	int nAngle, nSin, nCos, nOrigWidth, nOrigHeight, nRotWidth, nRotHeight;
	ImgRotationInfo(){
	}
	~ImgRotationInfo(){
	}
	inline void ConvertOriginal2Rotated(int &nX, int &nY)const{
		nX -= origCenter.x;
		nY -= origCenter.y;
		int xrot = (nX * nCos + nY * nSin) >> SINCOSFIXEBIT;
		int yrot = (nY * nCos - nX * nSin) >> SINCOSFIXEBIT;
		nX = xrot + rotCenter.x;
		nY = yrot + rotCenter.y;
	}

	inline void ConvertRotated2Original(int &nX, int &nY)const{
		nX -= rotCenter.x;
		nY -= rotCenter.y;
		int xrot = (nX * nCos - nY * nSin) >> SINCOSFIXEBIT;
		int yrot = (nY * nCos + nX * nSin) >> SINCOSFIXEBIT;
		nX = xrot + origCenter.x;
		nY = yrot + origCenter.y;
	}

	void GetRotateRoiCorners(const CRect & roi, vector<CPoint> & vCorners)const{
		vCorners.clear();
		if(roi.Width() <= 2 || roi.Height() < 2){
			vCorners.push_back(roi.CenterPoint());
			return;
		}
		int nOrigCenterX = origCenter. x << SINCOSFIXEBIT;
		int nOrigCenterY = origCenter. y << SINCOSFIXEBIT;

		int xs, ys, xsrc, ysrc;
		int nRotCenterX = rotCenter.x - roi.left;
		int nRotCenterY = rotCenter.y - roi.top;

		for(int i = 0; i < roi.Height(); i += roi.Height() - 1){
			for(int j = 0; j < roi.Width(); j +=  roi.Width() - 1){
				xs = j - nRotCenterX;
				ys = i - nRotCenterY;
				xsrc = xs * nCos - ys * nSin + nOrigCenterX;
				ysrc = ys * nCos + xs * nSin + nOrigCenterY;
				vCorners.push_back(CPoint(xsrc >> SINCOSFIXEBIT, ysrc >> SINCOSFIXEBIT));
			}
		}
	}

#define HORIZONTAL 0
#define VERTICAL   1

	int GetLinesOrientation()const{
		return (abs(nCos) > abs(nSin)) ? HORIZONTAL : VERTICAL;
	}

	CRect GetMinRoiDistancesFromImageFrame(const CRect & roi)const{
		vector<CPoint>  vCorners;
		GetRotateRoiCorners(roi, vCorners);
		vector<CPoint>::iterator i =  vCorners.begin();
		int nD;
		CRect minVals(i->x, i->y, nOrigWidth - 1 - i->x, nOrigHeight - 1 - i->y);
		for(++i; i !=  vCorners.end(); ++i){
			if(i->x < minVals.left) minVals.left = i->x;
			if((nD = nOrigWidth - 1 - i->x) < minVals.right) minVals.right = nD;
			if(i->y < minVals.top) minVals.top = i->y;
			if((nD = nOrigHeight - 1 - i->y) < minVals.bottom) minVals.bottom = nD;
		}
		return minVals;
	}

#define SCALE_FXPNT 8

	UCHAR *GetRotatedRoi1bpp(const UCHAR *pSrc, int nWidth, int nHeight, CRect roi, int nScale, 
		int nWindowSize, int nThreshold, int nMinBrightness, IplImage **ppGreyImg,
		UCHAR *pDestination = NULL, BOOL bWoB = FALSE)const{ 
			int nRoiW = (roi.Width() * nScale) >> SCALE_FXPNT;
			int nRoiH = (roi.Height() * nScale) >> SCALE_FXPNT;
			UCHAR *pDest = pDestination ? pDestination : new UCHAR[(nRoiW >> 3) * nRoiH];
			if(ppGreyImg) *ppGreyImg = cvCreateImage(cvSize((nRoiW >> 3) << 3, nRoiH), 8, 1);
			UCHAR *pGrey = ppGreyImg ? (UCHAR*)(*ppGreyImg)->imageData : NULL;
			int nCs = (nCos << SCALE_FXPNT) / nScale;
			int nSn = (nSin << SCALE_FXPNT) / nScale;
			int xs, ys, xsrc, ysrc;

			int nOrigCenterX = origCenter. x << SINCOSFIXEBIT;
			int nOrigCenterY = origCenter. y << SINCOSFIXEBIT;

			int nRotCenterX = ((rotCenter.x - roi.left) * nScale) >> SCALE_FXPNT;
			int nRotCenterY = ((rotCenter.y - roi.top) * nScale) >> SCALE_FXPNT;

			AdaptiveThresholdParams *pATParams = 
				CreateAdaptiveThresholdParams(nRoiW, nRoiH, nWindowSize, nThreshold, nMinBrightness, pDest);

			UCHAR *pD, *pG;
			for(int i=0; i<nRoiH; ++i){
				pD = pG = AdaptiveThresholdGetCurrSrcRow(pATParams);
				for(int j=0; j<nRoiW; ++j, ++pD){			
					xs = j - nRotCenterX;
					ys = i - nRotCenterY;
					xsrc = xs * nCs - ys * nSn + nOrigCenterX;
					ysrc = ys * nCs + xs * nSn + nOrigCenterY;
					if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
						&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
							*pD = GetImageByteBL(xsrc, ysrc, pSrc, nWidth, nHeight);
							if(bWoB) *pD = 255 - *pD;
					}
					else{
						*pD = 0;
					}
				}
				AdaptiveThresholdNextRow(pATParams);
				if(pGrey) {
					memcpy(pGrey, pG, nRoiW);
					pGrey += (*ppGreyImg)->widthStep;
				}
			}
			AdaptiveThresholdSummarize(&pATParams);
			return pDest;
	}

	template <class T>
	void GetSumOnTopAndLBottom(const UCHAR *pSrc, const T* frame, int & nSum, int & nCount)const{
		nSum = nCount = 0;
		int nRoiW = frame->right - frame->left;
		int nRoiH = frame->bottom - frame->top;
		int nWidth = nOrigWidth;
		int nHeight = nOrigHeight;

		int nCs = nCos;
		int nSn = nSin;
		int xs, ys, xsrc, ysrc;

		int nOrigCenterX = origCenter. x << SINCOSFIXEBIT;
		int nOrigCenterY = origCenter. y << SINCOSFIXEBIT;

		int nRotCenterX = rotCenter.x - frame->left;
		int nRotCenterY = rotCenter.y - frame->top;

		int i = -1, j;
		for(j=0; j<nRoiW; ++j){			
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					nSum += GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					++nCount;
			}
		}

		i = nRoiH + 1;
		for(j=0; j<nRoiW; ++j){			
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					nSum += GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					++nCount;
			}
		}
	}

	void GetSumOnFrame(const UCHAR *pSrc, CRect frame, int & nSum, int & nCount)const{
		nSum = nCount = 0;
		int nRoiW = frame.Width();
		int nRoiH = frame.Height();
		int nWidth = nOrigWidth;
		int nHeight = nOrigHeight;

		int nCs = nCos;
		int nSn = nSin;
		int xs, ys, xsrc, ysrc;

		int nOrigCenterX = origCenter. x << SINCOSFIXEBIT;
		int nOrigCenterY = origCenter. y << SINCOSFIXEBIT;

		int nRotCenterX = rotCenter.x - frame.left;
		int nRotCenterY = rotCenter.y - frame.top;

		int i = -1, j;
		for(j=0; j<nRoiW; ++j){			
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					nSum += GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					++nCount;
			}
		}

		i = nRoiH;
		for(j=0; j<nRoiW; ++j){			
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					nSum += GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					++nCount;
			}
		}

	}

	void GetMinMaxOnFrame(const UCHAR *pSrc, CRect frame, int & nMin, int & nMax, int & nCount)const{
		nCount = 0;
		int nRoiW = frame.Width();
		int nRoiH = frame.Height();
		int nWidth = nOrigWidth;
		int nHeight = nOrigHeight;

		int nCs = nCos;
		int nSn = nSin;
		int xs, ys, xsrc, ysrc;

		int nOrigCenterX = origCenter. x << SINCOSFIXEBIT;
		int nOrigCenterY = origCenter. y << SINCOSFIXEBIT;

		int nRotCenterX = rotCenter.x - frame.left;
		int nRotCenterY = rotCenter.y - frame.top;

		int i = 0, j;
		for(j=0; j<nRoiW; ++j){			
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					int nVal = GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					if(nCount < 1) {
						nMin = nMax = nVal;
					}
					if(nMin > nVal) nMin = nVal;
					if(nMax < nVal) nMax = nVal;
					++nCount;
			}
		}

		i = nRoiH;
		for(j=0; j<nRoiW; ++j){			
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					int nVal = GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					if(nCount < 1) {
						nMin = nMax = nVal;
					}
					if(nMin > nVal) nMin = nVal;
					if(nMax < nVal) nMax = nVal;
					++nCount;
			}
		}

		j = 0;
		for(i= 1; i<nRoiH - 1; ++i){
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					int nVal = GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					if(nCount < 1) {
						nMin = nMax = nVal;
					}
					if(nMin > nVal) nMin = nVal;
					if(nMax < nVal) nMax = nVal;
					++nCount;
			}
		}

		j = nRoiW - 1;
		for(i= 1; i<nRoiH - 1; ++i){
			xs = j - nRotCenterX;
			ys = i - nRotCenterY;
			xsrc = xs * nCs - ys * nSn + nOrigCenterX;
			ysrc = ys * nCs + xs * nSn + nOrigCenterY;
			if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)
				&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){
					int nVal = GetImageByteNN(xsrc, ysrc, pSrc, nWidth, nHeight);
					if(nCount < 1) {
						nMin = nMax = nVal;
					}
					if(nMin > nVal) nMin = nVal;
					if(nMax < nVal) nMax = nVal;
					++nCount;
			}
		}
	}
	template <class T>
	BOOL IsRotatedRoiBoW(const UCHAR *pSrc, const T* roi, int nAvgGreyLevel, int nMinThreshold = 25)const{
		int nSum = 0, nCount = 0;
		GetSumOnTopAndLBottom(pSrc, roi, nSum, nCount);
		if(nCount < 1) return FALSE;
		return nSum / nCount - nAvgGreyLevel >= nMinThreshold; 
	}

	template <class T>
	BOOL IsRotatedRoiWoB(const UCHAR *pSrc, const T* roi, int nAvgGreyLevel, int nMinThreshold = 25)const{ 
		int nSum = 0, nCount = 0;
		GetSumOnTopAndLBottom(pSrc, roi, nSum, nCount);
		if(nCount < 1) return FALSE;
		return nAvgGreyLevel - nSum / nCount >= nMinThreshold; 
	}

	template <class T>
	int GetAvgTopAndBottom(const UCHAR *pSrc, const T* roi)const{
		int nSum = 0, nCount = 0;
		GetSumOnTopAndLBottom(pSrc, roi, nSum, nCount);
		if(nCount < 1) return 0;
		return nSum / nCount; 
	}

#define BEGIN_CORECTION_LOOP_NO_SCALE(CurvatureCorectionFunction) \
	for(i=0; i < nRoiH; ++i){ \
	pD = pG = AdaptiveThresholdGetCurrSrcRow(pATParams);\
	for(j=0; j < nRoiW; ++j, ++pD){ \
	xs = j - nRotCenterX; \
	ys = CurvatureCorectionFunction(j - nHalfRoiW, i - nHalfRoiH, pCoefs) - nYCorr;\
	xsrc = xs * nCs - int((ys * nSn) >> CURVATURE_RES_SHIFT) + nOrigCenterX;\
	ysrc = int((ys * nCs) >> CURVATURE_RES_SHIFT) + xs * nSn + nOrigCenterY;\
	if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT)\
	&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){

#define BEGIN_CORECTION_LOOP_WITH_SCALE(CurvatureCorectionFunction) \
	int nOnrOvrScl = (1 << (SCALE_FXPNT * 2)) / nScale; \
	LONGLONG nX, nY; \
	for(i=0; i < nRoiH; ++i){ \
	pD = pG = AdaptiveThresholdGetCurrSrcRow(pATParams);\
	for(j=0; j < nRoiW; ++j, ++pD){ \
	xs = j - nRotCenterX; \
	nX = ((j - nHalfRoiW) * nOnrOvrScl) >> (SCALE_FXPNT - XY_IN_SHIFT); \
	nY = ((i - nHalfRoiH) * nOnrOvrScl) >> (SCALE_FXPNT - XY_IN_SHIFT); \
	ys = ((CurvatureCorectionFunction(nX, nY, pCoefs) * nScale) >> SCALE_FXPNT) - nYCorr; \
	xsrc = xs * nCs - int((ys * nSn) >> CURVATURE_RES_SHIFT) + nOrigCenterX; \
	ysrc = int((ys * nCs) >> CURVATURE_RES_SHIFT) + xs * nSn + nOrigCenterY;\
	if(xsrc >= (1<< SINCOSFIXEBIT) && ysrc >= (1<< SINCOSFIXEBIT) \
	&& xsrc < ((nWidth-1)<< SINCOSFIXEBIT) && ysrc < ((nHeight-1)<< SINCOSFIXEBIT)){ \

#define END_CORECTION_LOOP \
	}\
			else{\
			*pD = 0;\
	}\
	}\
	AdaptiveThresholdNextRow(pATParams);\
	if(pGrey) {\
		memcpy(pGrey, pG, nRoiW);\
		pGrey += (*ppGreyImg)->widthStep;\
	}\
	}

#define CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CurvatureCorectionFunction) \
	BEGIN_CORECTION_LOOP_NO_SCALE(CurvatureCorectionFunction)\
	*pD = GetImageByteBL(xsrc, ysrc, pSrc, nWidth, nHeight);\
	END_CORECTION_LOOP

#define CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CurvatureCorectionFunction) \
	BEGIN_CORECTION_LOOP_NO_SCALE(CurvatureCorectionFunction)\
	*pD = 255 - GetImageByteBL(xsrc, ysrc, pSrc, nWidth, nHeight);\
	END_CORECTION_LOOP

#define CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CurvatureCorectionFunction) \
	BEGIN_CORECTION_LOOP_WITH_SCALE(CurvatureCorectionFunction)\
	*pD = GetImageByteBL(xsrc, ysrc, pSrc, nWidth, nHeight);\
	END_CORECTION_LOOP

#define CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CurvatureCorectionFunction) \
	BEGIN_CORECTION_LOOP_WITH_SCALE(CurvatureCorectionFunction)\
	*pD = 255 - GetImageByteBL(xsrc, ysrc, pSrc, nWidth, nHeight);\
	END_CORECTION_LOOP

	UCHAR *GetRotatedRoi1bpp(const UCHAR *pSrc, int nWidth, int nHeight, CRect roi, int nScale, CPoint origin, int nOrderX, int nOrderY, LONGLONG *pCoefs,
		int nWindowSize, int nThreshold, int nMinBrightness, IplImage **ppGreyImg = NULL, UCHAR *pDestination = NULL, BOOL bWoB = FALSE)const{ 
			int nRoiW = (roi.Width() * nScale) >> SCALE_FXPNT;
			int nRoiH = (roi.Height() * nScale) >> SCALE_FXPNT;
			int nHalfRoiW = ((origin.x - roi.left) * nScale ) >> SCALE_FXPNT;
			int nHalfRoiH = ((origin.y - roi.top) * nScale ) >> SCALE_FXPNT;
			UCHAR *pDest = pDestination ? pDestination : new UCHAR[(nRoiW >> 3) * nRoiH];
			if(ppGreyImg) *ppGreyImg = cvCreateImage(cvSize((nRoiW >> 3) << 3, nRoiH), 8, 1);
			UCHAR *pGrey = ppGreyImg ? (UCHAR*)(*ppGreyImg)->imageData : NULL;
			int nCs = (nCos << SCALE_FXPNT) / nScale;
			int nSn = (nSin << SCALE_FXPNT) / nScale;
			LONGLONG ys;
			int xs, xsrc, ysrc;

			int nOrigCenterX = origCenter. x << SINCOSFIXEBIT;
			int nOrigCenterY = origCenter. y << SINCOSFIXEBIT;

			int nRotCenterX = ((rotCenter.x - roi.left) * nScale) >> SCALE_FXPNT;
			int nRotCenterY = ((rotCenter.y - roi.top) * nScale) >> SCALE_FXPNT;

			int nYCorr = (nRotCenterY - nHalfRoiH) << CURVATURE_RES_SHIFT;
			int i, j;
			UCHAR *pD, *pG;

			AdaptiveThresholdParams *pATParams = 
				CreateAdaptiveThresholdParams(nRoiW, nRoiH, nWindowSize, nThreshold, nMinBrightness, pDest);
			if(bWoB) {
				if(nScale == (1 << SCALE_FXPNT)){
					if(nOrderX == 1 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CalcYCorrection_1_1)
					}
					else if(nOrderX == 2 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CalcYCorrection_2_1)
					}
					else if(nOrderX == 2 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CalcYCorrection_2_2)
					}
					else if(nOrderX == 3 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CalcYCorrection_3_1)
					}
					else if(nOrderX == 3 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CalcYCorrection_3_2)
					}
					else if(nOrderX == 3 && nOrderY == 3){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_WoB(CalcYCorrection_3_3)
					}
				}
				else{
					if(nOrderX == 1 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CalcYCorrectionScale_1_1)
					}
					else if(nOrderX == 2 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CalcYCorrectionScale_2_1)
					}
					else if(nOrderX == 2 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CalcYCorrectionScale_2_2)
					}
					else if(nOrderX == 3 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CalcYCorrectionScale_3_1)
					}
					else if(nOrderX == 3 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CalcYCorrectionScale_3_2)
					}
					else if(nOrderX == 3 && nOrderY == 3){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_WoB(CalcYCorrectionScale_3_3)
					}
				}
			}
			else{
				if(nScale == (1 << SCALE_FXPNT)){
					if(nOrderX == 1 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CalcYCorrection_1_1)
					}
					else if(nOrderX == 2 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CalcYCorrection_2_1)
					}
					else if(nOrderX == 2 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CalcYCorrection_2_2)
					}
					else if(nOrderX == 3 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CalcYCorrection_3_1)
					}
					else if(nOrderX == 3 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CalcYCorrection_3_2)
					}
					else if(nOrderX == 3 && nOrderY == 3){
						CURVATURE_CORECTION_LOOP_NO_SCALE_1bpp_BoW(CalcYCorrection_3_3)
					}
				}
				else{
					if(nOrderX == 1 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CalcYCorrectionScale_1_1)
					}
					else if(nOrderX == 2 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CalcYCorrectionScale_2_1)
					}
					else if(nOrderX == 2 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CalcYCorrectionScale_2_2)
					}
					else if(nOrderX == 3 && nOrderY == 1){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CalcYCorrectionScale_3_1)
					}
					else if(nOrderX == 3 && nOrderY == 2){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CalcYCorrectionScale_3_2)
					}
					else if(nOrderX == 3 && nOrderY == 3){
						CURVATURE_CORECTION_LOOP_WITH_SCALE_1bpp_BoW(CalcYCorrectionScale_3_3)
					}
				}
			}
			AdaptiveThresholdSummarize(&pATParams);
			return pDest;
	}
};

class CHoughTransform  
{
	int m_roiW, m_roiH, m_roiCenterX, m_roiCenterY;
	int m_nImgWidth, m_nImgHeight;
	int m_nAngle, m_nTheta, m_nRho, m_nFirstAutoCorrPeak;
	int m_nFirstLine, m_nLastLine;
	int m_nFirstColumn, m_nLastColumn;
	int *m_pSin, *m_pCos, *m_pVote;
	double m_fTwiceMaxRho;
	//void GetTextROI(int &xcenter, int &ycenter, int &roiW, int &roiH);
	void RotateRoi(UCHAR *pSrc, int nWidth, int nHeight, 
		UCHAR *pDest, int roiW, int roiH, 
		int xcenter, int ycenter, int nSin, int nCos, BOOL bX2);
	//	void FindTextFirstAndLastLinePosition();
	//	void FindTextFirstAndLastColumnPosition();
	//	void FindROI();
	void GetRotatedSize(int nAngle, int nWidth, int nHeight, int & nRotW, int & nRotH);
	CPoint RotatedPointToOriginal(CPoint p, CPoint rotCenter, int nOrigHeight, 
		int rotW, int rotH, int nAngle, BOOL bX2 = FALSE);

public:
	void RotateRoiGL(const UCHAR *pSrc, UCHAR *pDest, const ImgRotationInfo &iri, BOOL bX2, BOOL bFlip);
	//void RotateRoiGL(UCHAR *pSrc, int nWidth, int nHeight, 
	//	UCHAR *pDest, int roiW, int roiH, 
	//	int xcenter, int ycenter, int nSin, int nCos, BOOL bX2, BOOL bFlip);
	inline int GetAngle()const{return 180 - m_nAngle;}
	void FindEdges(const char *pImg, int nWidth, int nHeight, BOOL bBlackOnWhite, BOOL bVertical,
		const vector<CPoint> & vPeaks, vector<float> & vEdges);
	//int GetVerticalShift(const UCHAR *pImg, int nWidth, int nHeight);
	pair<int, int> FindAngle(int nMaxPeakWidth, int nMinPeakWidth = 0);
	int FindAngle();
	//CPoint TranslateCoordinatesToTheOriginalImage(CPoint p);//p and pt shifted left by  SINCOSFIXEBIT

	//UCHAR *CreateCorrectedImageGL(UCHAR *pSrc, int nWidth, int nHeight, int nAngle,
	//	int &roiW, int &roiH, BOOL bAutoROI, int nThresh, BOOL bX2 = FALSE, BOOL bFlip = TRUE);
	UCHAR *CreateCorrectedImageGL(const UCHAR *pSrc, const ImgRotationInfo & iri,
			int nThresh, IplImage **ppGImg, IplImage ** ppBWImg);
	void GetRoi(const UCHAR *pOpnImg, int nWidth, int nHeight, int & xcenter, int & ycenter, int & roiW, int & roiH);
	//UCHAR *CreateCorrectedImage(UCHAR *pSrc, int nWidth, int nHeight, int nAngle,
	//	int &roiW, int &roiH, BOOL bAutoROI, BOOL bX2 = FALSE);
	int GetRotationAngle(const char *pImg, int nWidth, int nHeight, BOOL bCenterOnly);
	//int GetRotationAngle(const char *pImg, int nWidth, int nHeight);
	//int GetRotationAngle(const char *pImg, int nWidth, int nHeight, BOOL bCenterOnly, BOOL bMorphOpen);
	int Transform(const char *pImg, int nWidth, int nHeight, CRect roi, int *pLimits = NULL, BOOL bBlackOnWhite = TRUE);
	int Transform(const char *pImg, int nWidth, int nHeight, BOOL bCenterOnly, BOOL bBlackOnWhite = TRUE);
	int Transform(const char *pImg, int nWidth, int nHeight, CRect roi, float fMaxRho2HeightRatio, int *pLimits = NULL,
		BOOL bBlackOnWhite = TRUE);
	bool GetRotationAngles(const char *pImg, int nWidth, int nHeight, vector<int> & vAngles);
	int FindFirstAutoCorrelationPeak()const;
	BOOL FindPeaks(vector<CPoint> &vPeaks,  int nMinPeakVal = 0);
	CHoughTransform(int nTheta = 180, int nDist = 200);
	void FillRotationInfo(int nAngle, int nWidth, int nHeight, ImgRotationInfo &iri);
	BOOL GetTwoPagesInBookDivider(const UCHAR *pOpnImg, int nWidth, int nHeight, int & xtop, int & xbottom);
	CPoint  GetMinRoiDistanceFromImageFrame(const CRect & roi)const;
	void GetRotateRoiCorners(const CRect & roi, vector<CPoint> & vCorners)const;
	CPoint TranslateCoordinatesToTheOriginalImage(CPoint p, BOOL bX2 = FALSE)const;
	virtual ~CHoughTransform();

private:
	void SaveVote();
};

#endif //__HOUGHTRANSFORM_H__
