// ImageOps.cpp: implementation of the ImageOps class.
// by Jason Plumb
//
//////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ImageOps.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ImageOps::ImageOps()
{

}

ImageOps::~ImageOps()
{

}

/*	This function will return a pointer to the specified row.
	It is assumed that the row number desired is zero based and the 
	height is 1 based. 
	This is a dumb function used for */
unsigned char* ImageOps::getRow(unsigned char *pImgBuff, int iWidth, int iHeight, int iRowNum)
{
	if((pImgBuff == NULL) || (iRowNum >= iHeight))
		return NULL;									//Error (bad ptr or row number)

	return (unsigned char*)&pImgBuff[ iWidth*iRowNum];	//Isn't this function fairly stupid?

}//getRow method


int ImageOps::doBlob(unsigned char **pMatrix, int iWidth, int iHeight,/* int iMaxGray,*/
					 unsigned char **pBlobMatrix, unsigned char **pBMatrix, int *piCt){

	int i;

	if((pMatrix == NULL) || (*pMatrix == NULL) || (iWidth <=0) || (iHeight <=0) ||
		(pBlobMatrix == NULL) || (*pBlobMatrix == NULL) ||
		(pBMatrix == NULL) || (*pBMatrix == NULL) || (piCt == NULL) || (*piCt <= 0))
		return -1;			//Bad input parameter

	//Ensure blob matrix is empty
	for(i=0;i<iHeight;i++){
		memset(pBlobMatrix[i], 0, iWidth*sizeof(unsigned char));
	}//for

	for(i=0;i<*piCt;i++){
		memset(pBMatrix[i], 0, *piCt*sizeof(unsigned char));
		pBMatrix[i][i] = 1;		//Ensure diagonals are 1
	}//for
	
	int iRow, iCol, x, y;
	unsigned char iCurrLabel = FIRST_LABEL;

	for(iRow = 0; iRow < iHeight; iRow++){				//Loop for all rows

		for(iCol = 0; iCol < iWidth; iCol++){

			if(pMatrix[iRow][iCol]){
				unsigned char r, t, q, s;				//Pixel neighbors [8 connected]
				r = t = q = s = 0;						//Default/unassigned value

				if(iCol > 0)
					t = pMatrix[iRow][iCol-1];
				if(iRow > 0)
					r = pMatrix[iRow-1][iCol];
				if((iRow>0) && (iCol>0))
					q = pMatrix[iRow-1][iCol-1];
				if((iRow>0) && (iCol<(iWidth-1)))
					s = pMatrix[iRow-1][iCol+1];


				if(( r | t | q | s ) == 0x00){				//All neighbors are black
					pBlobMatrix[iRow][iCol] = iCurrLabel;	//Assign new label to p
					iCurrLabel++;
					continue;
				}//if

//				if( r & (( t | q | s)==0)){			//only r is 1
				if( r ){
					if(( t | q | s)==0){
						pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow-1][iCol];
						continue;
					}//if

					x = pBlobMatrix[iRow-1][iCol] - FIRST_LABEL;
					if(x >= *piCt)
						return ERROR_LABELMAX;						//Ran out of labels...

					if( t ){
						y = pBlobMatrix[iRow][iCol-1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( q ){
						y = pBlobMatrix[iRow-1][iCol-1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( s ){
						y = pBlobMatrix[iRow-1][iCol+1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if

					pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow-1][iCol];
					continue;

				}//if

//				if( t & (( r | q | s)==0)){			//only t is 1
				if( t ){
					if(( r | q | s)==0){
						pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow][iCol-1];
						continue;
					}//if

					x = pBlobMatrix[iRow][iCol-1] - FIRST_LABEL;
					if(x >= *piCt)
						return ERROR_LABELMAX;						//Ran out of labels...

					if( r ){
						y = pBlobMatrix[iRow-1][iCol] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( q ){
						y = pBlobMatrix[iRow-1][iCol-1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( s ){
						y = pBlobMatrix[iRow-1][iCol+1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					
					pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow][iCol-1];
					continue;

				}//if t

//				if( q & (( r | s | t)==0)){			//only q is 1
				if( q ){
					if(( r | t | s)==0){
						pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow-1][iCol-1];
						continue;
					}//if

					x = pBlobMatrix[iRow-1][iCol-1] - FIRST_LABEL;
					if(x >= *piCt)
						return ERROR_LABELMAX;						//Ran out of labels...

					if( r ){
						y = pBlobMatrix[iRow-1][iCol] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( t ){
						y = pBlobMatrix[iRow][iCol-1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( s ){
						y = pBlobMatrix[iRow-1][iCol+1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					
					pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow-1][iCol-1];
					continue;
					
				}//if q

				//if( s & (( q | r | t)==0)){			//only s is 1
				if( s ){
					if(( r | t | q)==0){
						pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow-1][iCol+1];
						continue;
					}//if

					x = pBlobMatrix[iRow-1][iCol+1] - FIRST_LABEL;
					if(x >= *piCt)
						return ERROR_LABELMAX;						//Ran out of labels...

					if( r ){
						y = pBlobMatrix[iRow-1][iCol] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( t ){
						y = pBlobMatrix[iRow][iCol-1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
					if( q ){
						y = pBlobMatrix[iRow-1][iCol-1] - FIRST_LABEL;						
						pBMatrix[x][y] = pBMatrix[y][x] = 1;
					}//if
	
					pBlobMatrix[iRow][iCol] = pBlobMatrix[iRow-1][iCol+1];
					continue;
					
				}//if s


				//If we made it here, we know more than 2 neighbors are 1...we just
				// don't know which ones yet
				int iError = -1;


			}//if (pixel is max value, white)

			if(iCurrLabel > LAST_LABEL)				//Serious problems
				return -1;
		}//for
		if(iCurrLabel > LAST_LABEL)					//Serious problems
			return -1;
	}//for

	*piCt = iCurrLabel - FIRST_LABEL;
	return *piCt;

}//doBlob

//This function will resolv the equivalencies and return the number of objects
//The BMat matrix must have been populated correctly, and diagonals must be 1 (minimally), or
//this may crash.
int ImageOps::resolvEquiv(unsigned char **pBlobMat, int iWidth, int iHeight, unsigned char **pBMat, int *piCt){

	if( (pBlobMat==NULL) || (*pBlobMat==NULL) || (iWidth <=0) || (iHeight<=0) ||
		(pBMat==NULL) || (*pBMat==NULL) || (piCt == NULL) || (*piCt <0))
		return -1;			//Bad param


	bToBPlus(pBMat, *piCt);					//Modify to create transitive closure

	int iRow, iCol;
	int iObjCt = 0;
	unsigned char buffer[1000];				//Assumes no more than 1000 blobs per image
	memset(buffer, 0, 1000);
	
	unsigned char *pEquiv = (unsigned char *)malloc(*piCt);
	for(iCol=0;iCol<*piCt;iCol++){								//Create equivalency class mappings...
		iRow = 0;
		while( pBMat[iRow][iCol] == 0)
			iRow++;
		pEquiv[iCol] = iRow + FIRST_LABEL;

		if(memchr(buffer, (iRow+FIRST_LABEL), iObjCt)==NULL){
			buffer[iObjCt] = (iRow+FIRST_LABEL);
			iObjCt++;
		}//if

	}//for

	int i, j;
	for(i=0;i < iObjCt;i++){					//Realign objects to first label number (close gaps)
		for(j=0; j< *piCt; j++){
			if(pEquiv[j] == buffer[i])
				pEquiv[j] = i+FIRST_LABEL;
		}//for
		

	}//for

	*piCt = iObjCt;

	for(iRow=0; iRow < iHeight; iRow++){
		for(iCol=0; iCol < iWidth; iCol++){
			if(pBlobMat[iRow][iCol])
				pBlobMat[iRow][iCol] = pEquiv[ pBlobMat[iRow][iCol] - FIRST_LABEL];
		}//for
	}//for
							
	free(pEquiv);

	return *piCt;

}//resolvEquiv

//This is used to modify the B matrix to B+ (transitive closure).  It is used by 
//the resolvEquiv method
void ImageOps::bToBPlus(unsigned char **pB, int iCt){

	int i,j,k;
	if((pB==NULL) || (*pB==NULL) || (iCt <=0))
		return;

	for(j=0;j<iCt;j++){
		for(i=0;i<iCt;i++){
			if(pB[j][i]){
				for(k=0;k<iCt;k++){
					pB[k][i] = (pB[k][i] | pB[k][j]);
				}//for
			}//if
		}//for
	}//for

}//bToBPlus


void ImageOps::debugOutputBlobMatrix(char *pFilename, unsigned char **pBlobMatrix, int iWidth, int iHeight, unsigned char **pBMatrix, int iCt){

	return;
	/*
	if((pFilename==NULL) || (pBlobMatrix==NULL) || (*pBlobMatrix==NULL) || (iWidth <=0) || (iHeight<=0) ||
		(pBMatrix==NULL) || (*pBMatrix==NULL) || (iCt <0))
		return;

	FILE *out = fopen(pFilename, "at");
	if(out == NULL)
		return;

	int i,j;
	fprintf(out, "------------- Begin New Image ----------------\n");
	for(i=0;i<iHeight;i++){
		for(j=0;j<iWidth;j++){
			if(pBlobMatrix[i][j])
				fprintf(out, "%c", pBlobMatrix[i][j]);
			else
				fprintf(out, " ");
		}
		fprintf(out, "\n");
	}//for
	fprintf(out, "\n");

	// Print the equiv matrix
	fprintf(out, " Equivalency matrix:  ");
	for(i=0;i<iCt;i++)
		fprintf(out, " %c  ", i+FIRST_LABEL);
	fprintf(out, "\n                      ");
	for(i=0;i<iCt;i++)
		fprintf(out, "----");

	for(i=0;i<iCt;i++){
		fprintf(out, "\n                   %c |", i+FIRST_LABEL);
		for(j=0;j<iCt;j++){
			if(pBMatrix[i][j])
				fprintf(out, " 1  ");
			else
				fprintf(out, " 0  ");
		}//for
	}//for
	fprintf(out, "\n");
	if(iCt>0)
		fprintf(out, "\n");

	fclose(out);
	*/
}//debugOutputBlobMatrix

int ImageOps::getBlobWidth(unsigned char **pBlobMat, int iWidth, int iHeight, unsigned char label){
	int iLeft, iRight;
	iLeft = iRight = -1;

	if((pBlobMat==NULL) || (*pBlobMat==NULL) || (iWidth <=0) || (iHeight<=0))
		return -1;

	for(int iRow=0;iRow<iHeight;iRow++){
		for(int iCol=0;iCol<iWidth;iCol++){
			if(pBlobMat[iRow][iCol] == label){

				if((iLeft==-1) && (iRight==-1)){
					iLeft = iCol;
					iRight = iCol;
				}
				else if(iCol < iLeft)
					iLeft = iCol;
				else if(iCol > iRight)
					iRight = iCol;

			}//if
		}//for
	}//for

	if((iLeft == -1) && (iRight == -1))
		return 0;								//Didn't find a blob with this label
	else 
		return iRight-iLeft+1;
}//getBlobWidth method

int ImageOps::getBlobHeight(unsigned char **pBlobMat, int iWidth, int iHeight, unsigned char label){
	int iTop, iBottom;
	iTop = iBottom = -1;

	if((pBlobMat==NULL) || (*pBlobMat==NULL) || (iWidth <=0) || (iHeight<=0))
		return -1;

	for(int iRow=0;iRow<iHeight;iRow++){
		for(int iCol=0;iCol<iWidth;iCol++){
			if(pBlobMat[iRow][iCol] == label){

				if((iTop==-1) && (iBottom==-1)){
					iTop = iRow;
					iBottom = iRow;
				}
				else if(iRow < iTop)
					iTop = iRow;
				else if(iRow > iBottom)
					iBottom = iRow;

			}//if
		}//for
	}//for

	if((iTop == -1) && (iBottom == -1))
		return 0;								//Didn't find a blob with this label
	else 
		return iBottom-iTop+1;
}

int ImageOps::getBlobArea(unsigned char **pBlobMat, int iWidth, int iHeight, unsigned char label){

	//Note: This could have been implemented easier by calling getWidth and getHeight,
	//      but that would require 2 passes through the image.  This implementation only requires
	//      1 pass through.
	int iLeft, iRight, iTop, iBottom;
	iLeft = iRight = iTop = iBottom = -1;

	if((pBlobMat==NULL) || (*pBlobMat==NULL) || (iWidth <=0) || (iHeight<=0))
		return -1;

	for(int iRow=0;iRow<iHeight;iRow++){
		for(int iCol=0;iCol<iWidth;iCol++){
			if(pBlobMat[iRow][iCol] == label){

				if((iLeft==-1) && (iRight==-1)){
					iLeft = iCol;
					iRight = iCol;
				}
				else if(iCol < iLeft)
					iLeft = iCol;
				else if(iCol > iRight)
					iRight = iCol;

				if((iTop==-1) && (iBottom==-1)){
					iTop = iRow;
					iBottom = iRow;
				}
				else if(iRow < iTop)
					iTop = iRow;
				else if(iRow > iBottom)
					iBottom = iRow;

			}//if
		}//for
	}//for

	if((iLeft == -1) && (iRight == -1))
		return 0;								//Didn't find a blob with this label
	else 
		return (iRight-iLeft+1)*(iBottom-iTop+1);

}//getBlobArea





/*
//First attempt at something to pull connected components in a binary image
void ImageOps::getConnComps(unsigned char *pImgBuff, int iWidth, int iHeight, unsigned char *pGrayVals, int iGrayCt)
{

	if((pImgBuff == NULL) || (iWidth <= 0) || (iHeight <= 0) || (pGrayVals == NULL))
		return;		//bail out

	unsigned char *pPrevRow = NULL;
	unsigned char *pCurrRow = NULL;
	int iRow, iCol;

	int iLabelCt = 0;
	char **pLabels;
	pLabels = (char**)malloc(iWidth*iHeight*sizeof(char*));
	memset(pLabels, NULL, (iWidth*iHeight)*sizeof(char*));

	long *lLabelIndexes = (long*)malloc(iWidth*iHeight*sizeof(long));

	for(iRow = 0; iRow < iHeight; iRow++){			//Loop for all rows
		
		if(iRow != 0)
			pPrevRow = (unsigned char*)&pImgBuff[iWidth*(iRow-1)];
		else
			pPrevRow = NULL;			//No previous row...

		pCurrRow = (unsigned char*)&pImgBuff[iWidth*iRow];

		for(iCol = 0;iCol < iWidth; iCol++){		//Loop for all pixels in this row...
			int r = -1;				//Upper neighbor
			int t = -1;				//Left hand neighbor

			//TODO:  I think this should check the gray set instead of this...
			if(pCurrRow[iCol] == 0xFF){					//If it's 1

				if(iCol > 0)
					t = pCurrRow[iCol-1];
				if(iRow > 0)
					r = pPrevRow[iCol];
				
				if((r == 0x00) && (t == 0x00)){			//Assign new label to p
					pLabels[ (iWidth*iRow)+iCol] = (char*)malloc(20);
					sprintf(pLabels[(iWidth*iRow)+iCol], "label_%d_%d", iRow, iCol);
					lLabelIndexes[iLabelCt] = (iWidth*iRow)+iCol;
					iLabelCt++;
				}//if
				else if((r == 0xFF) && (t != 0xFF)){
					lLabelIndexes[
					strcpy(pLabels[(iWidth*iRow)+iCol], pLabels[(iWidth*(iRow-1))+iCol]);
				}//else if
				else if((t == 0xFF) && (r != 0xFF)){
					pLabels[ iRow*iCol] = (char*)malloc(20);
					strcpy(pLabels[(iWidth*iRow)+iCol], pLabels[(iWidth*iRow)+iCol-1]);
				}//else if
				else if((t == 0xFF) && (r == 0xFF)){		//both are 1
					
					char *pT = pLabels[(iWidth*iRow)+iCol-1];
					char *pR = pLabels[(iWidth*(iRow-1))+iCol];

					if(strcmp(pR, pT)==0){
						pLabels[ iRow*iCol] = (char*)malloc(20);
						strcpy(pLabels[iRow*iCol], pT);
					}//if
					else{			//equivalent (r region joins to t region through p)
						//TODO:  Implement this part...
					}



				}//else if



			}//if (pixel is a 1

		}//for


	}//for
	//free up memory here...
	

}//getConnComps method
*/











