//***********************************************************//
//* Blob analysis package  8 August 2003                    *//
//* Version 1.0                                             *//
//* Input: IplImage* binary image                           *//
//* Output: attributes of each connected region             *//
//* Author: Dave Grossman                                   *//
//* Modifications: Francesc Pinyol and Ricard Borras		*//
//* Email: dgrossman@cdr.stanford.edu                       *//
//* Email: fpinyol@cvc.uab.es rborras@cvc.uab.es            *//
//* Acknowledgement: the algorithm has been around > 20 yrs *//
//***********************************************************//

//! Indica si la connectivitat es a 8 (si es desactiva es a 4)
#define B_CONNECTIVITAT_8

//! si la imatge és cíclica verticalment (els blobs que toquen
//! les vores superior i inferior no es consideren externs)
#define IMATGE_CICLICA_VERTICAL		1
//! si la imatge és cíclica horitzontalment (els blobs que toquen
//! les vores dreta i esquerra no es consideren externs)
#define IMATGE_CICLICA_HORITZONTAL	0

#define PERIMETRE_DIAGONAL (1.41421356237310 - 2)
#define SQRT2	1.41421356237310
// color dels píxels de la màscara per ser exteriors
#define PIXEL_EXTERIOR 0


#include "blobresult.h"
#include "blobextraction.h"

/**
- FUNCIÓ: BlobAnalysis
- FUNCIONALITAT: Extreu els blobs d'una imatge d'un sol canal
- PARÀMETRES:
	- inputImage: Imatge d'entrada. Ha de ser d'un sol canal
	- threshold: Nivell de gris per considerar un pixel blanc o negre
	- maskImage: Imatge de màscara fora de la cual no es calculen els blobs. A més,
				 els blobs que toquen els pixels de la màscara a 0, són considerats
				 externs
	- borderColor: Color del marc de la imatge (0=black or 1=white)
	- findmoments: calcula els moments dels blobs o no
	- RegionData: on es desarà el resultat
- RESULTAT:
	- retorna true si tot ha anat bé, false si no. Deixa el resultat a blobs.
- RESTRICCIONS:
	- La imatge d'entrada ha de ser d'un sol canal
- AUTOR: dgrossman@cdr.stanford.edu
- DATA DE CREACIÓ: 25-05-2005.
- MODIFICACIÓ: Data. Autor. Descripció.
	- fpinyol@cvc.uab.es, rborras@cvc.uab.es: adaptació a les OpenCV
*/
bool BlobAnalysis(	IplImage* inputImage,
					uchar threshold,
				    IplImage* maskImage,
				    bool borderColor,
				    bool findmoments,
					blob_vector &RegionData )	
{
	// dimensions of input image taking in account the ROI
	int Cols, Rows, startCol, startRow;

	if( inputImage->roi )
	{
		CvRect imageRoi = cvGetImageROI( inputImage );
		startCol = imageRoi.x;
		startRow = imageRoi.y;
		Cols = imageRoi.width;
		Rows = imageRoi.height;
	}
	else
	{
		startCol = 0;
		startRow = 0; 
		Cols = inputImage->width;
		Rows = inputImage->height;
	}

	int Trans = Cols;				// MAX trans in any row
	char* pMask;
	char* pImage;

	// Convert image array into transition array. In each row
	// the transition array tells which columns have a color change
	int iCol,iRow,iTran, Tran;				// Data for a given run
	bool ThisCell, LastCell;		// Contents (colors (0 or 1)) within this row
	int TransitionOffset = 0;		// Performance booster to avoid multiplication
	
	// row 0 and row Rows+1 represent the border
	int i;
	int *Transition;				// Transition Matrix

	int nombre_pixels_mascara = 0;
	//! Imatge amb el perimetre extern de cada pixel
	IplImage *imatgePerimetreExtern;

	// input images must have only 1-channel and be an image
	if( !CV_IS_IMAGE( inputImage ) || (inputImage->nChannels != 1) )
	{
		return false;
	}
	if( maskImage != NULL )
	{
		// input image and mask are a valid image?
		if( !CV_IS_IMAGE( inputImage ) || !CV_IS_IMAGE( maskImage )) 
			return false;

		// comprova que la màscara tingui les mateixes dimensions que la imatge
		if( !CV_ARE_SIZES_EQ(inputImage, maskImage ) )
		{
			return false;
		}

		// comprova que la màscara sigui una imatge d'un sol canal (grayscale)
		if( maskImage->nChannels != 1 )
		{
			return false;
		}
		
	}

	// Initialize Transition array
	Transition=new int[(Rows + 2)*(Cols + 2)];
	memset(Transition,0,(Rows + 2) * (Cols + 2)*sizeof(int));
	Transition[0] = Transition[(Rows + 1) * (Cols + 2)] = Cols + 2;
	
	// Start at the beginning of the image (startCol, startRow)
	pImage = inputImage->imageData + startCol - 1 + startRow * inputImage->widthStep;

/*
	Paral·lelització del càlcul de la matriu de transicions
	Fem que cada iteració del for el faci un thread o l'altre ( tenim 2 possibles threads )	
*/
	if(maskImage == NULL)
	{
		imatgePerimetreExtern = NULL;

		//Fill Transition array
		for(iRow = 1; iRow < Rows + 1; iRow++)		// Choose a row of Bordered image
		{
			TransitionOffset = iRow*(Cols + 2); //per a que sigui paral·litzable
			iTran = 0;					// Index into Transition array
			Tran = 0;					// No transitions at row start
			LastCell = borderColor;

			for(iCol = 0; iCol < Cols + 2; iCol++)	// Scan that row of Bordered image
			{
				if(iCol == 0 || iCol == Cols+1) 
					ThisCell = borderColor;
				else
					ThisCell = ((unsigned char) *(pImage)) > threshold;

				if(ThisCell != LastCell)
				{
					Transition[TransitionOffset + iTran] = Tran;	// Save completed Tran
					iTran++;						// Prepare new index
					LastCell = ThisCell;			// With this color
				}

				Tran++;	// Tran continues
				pImage++;
			}
			
			Transition[TransitionOffset + iTran] = Tran;	// Save completed run
			if ( (TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2) )
			{
				Transition[TransitionOffset + iTran + 1] = -1;
			}
			//jump to next row (beginning from (startCol, startRow))
			pImage = inputImage->imageData - 1 + startCol + (iRow+startRow)*inputImage->widthStep;
		}
	}
	else
	{
		//maskImage not NULL: Cal recòrrer la màscara també per calcular la matriu de transicions

		char perimeter;
		char *pPerimetre;
		
		// creem la imatge que contindrà el perimetre extern de cada pixel
		imatgePerimetreExtern = cvCreateImage( cvSize(maskImage->width, maskImage->height), IPL_DEPTH_8U, 1);
		cvSetZero( imatgePerimetreExtern );

		pMask = maskImage->imageData - 1;
		
		//Fill Transition array
		for(iRow = 1; iRow < Rows + 1; iRow++)		// Choose a row of Bordered image
		{
			TransitionOffset = iRow*(Cols + 2);
			iTran = 0;					// Index into Transition array
			Tran = 0;					// No transitions at row start
			LastCell = borderColor;

			pPerimetre = imatgePerimetreExtern->imageData + (iRow - 1) * imatgePerimetreExtern->widthStep;
			//pMask = maskImage->imageData + (iRow-1) * maskImage->widthStep;

			for(iCol = 0; iCol < Cols + 2; iCol++)	// Scan that row of Bordered image
			{
				if(iCol == 0 || iCol == Cols+1 || ((unsigned char) *pMask) == PIXEL_EXTERIOR) 
					ThisCell = borderColor;
				else
					ThisCell = ((unsigned char) *(pImage)) > threshold;

				if(ThisCell != LastCell)
				{
					Transition[TransitionOffset + iTran] = Tran;	// Save completed Tran
					iTran++;						// Prepare new index
					LastCell = ThisCell;			// With this color
				}

				/*////////////////////////////////////////////////////////////////////////
				Calcul de la imatge amb els pixels externs
				////////////////////////////////////////////////////////////////////////*/
				// pels pixels externs no cal calcular res pq no hi accedir-hem
				if( (iCol > 0) && (iCol < Cols) )
				{
					if( *pMask == PIXEL_EXTERIOR )
					{
						*pPerimetre = 0;
					}
					else
					{
						perimeter = 0;
						
						// pixels al nord de l'actual
						if(iRow>1)
						{
							if( *(pMask - maskImage->widthStep ) == PIXEL_EXTERIOR) perimeter++;
						}

						// pixels a l'est i oest de l'actual
						if( iRow < imatgePerimetreExtern->height )
						{
							if( (iCol>0) && (*(pMask-1) == PIXEL_EXTERIOR) ) perimeter++;

							if( ( iCol < imatgePerimetreExtern->width - 1) && (*(pMask+1) == PIXEL_EXTERIOR) ) perimeter++;
						}

						// pixels al sud de l'actual
						if( iRow < imatgePerimetreExtern->height - 1)
						{
							if( (*(pMask+maskImage->widthStep) == PIXEL_EXTERIOR) ) perimeter++;
						}
				
						*pPerimetre = perimeter;
					}
				}

				Tran++;	// Tran continues
				pImage++;
				pMask++;
				pPerimetre++;
			}
			Transition[TransitionOffset + iTran] = Tran;	// Save completed run

			if ( (TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2) )
			{
				Transition[TransitionOffset + iTran + 1] = -1;
			}

			
			//jump to next row (beginning from (startCol, startRow))
			pImage = inputImage->imageData - 1 + startCol + (iRow+startRow)*inputImage->widthStep;
			//the mask should be the same size as image Roi, so don't take into account the offset
			pMask = maskImage->imageData - 1 + iRow*maskImage->widthStep;
		}
	}

	// Process transition code depending on Last row and This row
	//
	// Last ---++++++--+++++++++++++++-----+++++++++++++++++++-----++++++-------+++---
	// This -----+++-----++++----+++++++++----+++++++---++------------------++++++++--
	//
	// There are various possibilities:
	//
	// Case     1       2       3       4       5       6       7       8
	// Last |xxx    |xxxxoo |xxxxxxx|xxxxxxx|ooxxxxx|ooxxx  |ooxxxxx|    xxx|
	// This |    yyy|    yyy|  yyyy |  yyyyy|yyyyyyy|yyyyyyy|yyyy   |yyyy   |
	// Here o is optional
	// 
	// Here are the primitive tests to distinguish these 6 cases:
	//   A) Last end < This start - 1 OR NOT		Note: -1
	//   B) This end < Last start OR NOT
	//   C) Last start < This start OR NOT
	//   D) This end < Last end OR NOT
	//   E) This end = Last end OR NOT
	//
	// Here is how to use these tests to determine the case:
	//   Case 1 = A [=> NOT B AND C AND NOT D AND NOT E]
	//   Case 2 = C AND NOT D AND NOT E [AND NOT A AND NOT B]
	//   Case 3 = C AND D [=> NOT E] [AND NOT A AND NOT B]
	//   Case 4 = C AND NOT D AND E [AND NOT A AND NOT B]
	//   Case 5 = NOT C AND E [=> NOT D] [AND NOT A AND NOT B]
	//   Case 6 = NOT C AND NOT D AND NOT E [AND NOT A AND NOT B]
	//   Case 7 = NOT C AND D [=> NOT E] [AND NOT A AND NOT B]
	//   Case 8 = B [=> NOT A AND NOT C AND D AND NOT E]
	//
	// In cases 2,3,4,5,6,7 the following additional test is needed:
	//   Match) This color = Last color OR NOT
	//
	// In cases 5,6,7 the following additional test is needed:
	//   Known) This region was already matched OR NOT
	//
	// Here are the main tests and actions:
	//   Case 1: LastIndex++;
	//   Case 2: if(Match) {y = x;}
	//           LastIndex++;
	//   Case 3: if(Match) {y = x;}
	//           else {y = new}
	//           ThisIndex++;
	//   Case 4: if(Match) {y = x;}
	//           else {y = new}
	//           LastIndex++;
	//           ThisIndex++;
	//   Case 5: if(Match AND NOT Known) {y = x}
	//           else if(Match AND Known) {Subsume(x,y)}
	//           LastIndex++;ThisIndex++
	//   Case 6: if(Match AND NOT Known) {y = x}
	//           else if(Match AND Known) {Subsume(x,y)}
	//           LastIndex++;
	//   Case 7: if(Match AND NOT Known) {y = x}
	//           else if(Match AND Known) {Subsume(x,y)}
	//           ThisIndex++;
	//   Case 8: ThisIndex++;

	int *SubsumedRegion = NULL;			
	
	double ThisParent;	// These data can change when the line is current
	double ThisArea;
	double ThisPerimeter;
	double ThisSumX;
	double ThisSumY;
	double ThisSumXX;
	double ThisSumYY;
	double ThisSumXY;
	double ThisMinX;
	double ThisMaxX;
	double ThisMinY;
	double ThisMaxY;
	double LastPerimeter;	// This is the only data for retroactive change
	double ThisExternPerimeter;
	
	int HighRegionNum = 0;
	int RegionNum = 0;
	int ErrorFlag = 0;
	
	int LastRow, ThisRow;			// Row number
	int LastStart, ThisStart;		// Starting column of run
	int LastEnd, ThisEnd;			// Ending column of run
	int LastColor, ThisColor;		// Color of run
	
	int LastIndex, ThisIndex;		// Which run are we up to
	int LastIndexCount, ThisIndexCount;	// Out of these runs
	int LastRegionNum, ThisRegionNum;	// Which assignment
	int *LastRegion;				// Row assignment of region number
	int *ThisRegion;		// Row assignment of region number
	
	int LastOffset = -(Trans + 2);	// For performance to avoid multiplication
	int ThisOffset = 0;				// For performance to avoid multiplication
	int ComputeData;

	CvPoint actualedge;
	uchar imagevalue;
	bool CandidatExterior = false;

	// apuntadors als blobs de la regió actual i last
	CBlob *regionDataThisRegion, *regionDataLastRegion;
	
	LastRegion=new int[Cols+2];
	ThisRegion=new int[Cols+2];

	for(i = 0; i < Cols + 2; i++)	// Initialize result arrays
	{
		LastRegion[i] = -1;
		ThisRegion[i] = -1;
	}

	//create the external blob
	RegionData.push_back( new CBlob() );
	SubsumedRegion = NewSubsume(SubsumedRegion,0);
	RegionData[0]->parent = -1;
	RegionData[0]->area = (double) Transition[0];
	RegionData[0]->perimeter = (double) (2 + 2 * Transition[0]);

	ThisIndexCount = 1;
	ThisRegion[0] = 0;	// Border region

	// beginning of the image 
	// en cada linia, pimage apunta al primer pixel de la fila
	pImage = inputImage->imageData - 1 + startCol + startRow * inputImage->widthStep;
	//the mask should be the same size as image Roi, so don't take into account the offset
	if(maskImage!=NULL) pMask = maskImage->imageData - 1;

	char *pImageAux, *pMaskAux;
	
	// Loop over all rows
	for(ThisRow = 1; ThisRow < Rows + 2; ThisRow++)
	{
		//cout << "========= THIS ROW = " << ThisRow << endl;	// for debugging
		ThisOffset += Trans + 2;
		ThisIndex = 0;
		LastOffset += Trans + 2;;
		LastRow = ThisRow - 1;
		LastIndexCount = ThisIndexCount;
		LastIndex = 0;

		int EndLast = 0;
		int EndThis = 0;

		for(int j = 0; j < Trans + 2; j++)
		{
			int Index = ThisOffset + j;
			int TranVal = Transition[Index];
			if(TranVal > 0) ThisIndexCount = j + 1;	// stop at highest 

			if(ThisRegion[j] == -1)  { EndLast = 1; }
			if(TranVal < 0) { EndThis = 1; }

			if(EndLast > 0 && EndThis > 0) { break; }

			LastRegion[j] = ThisRegion[j];
			ThisRegion[j] = -1;		// Flag indicates region is not initialized
		}

		int MaxIndexCount = LastIndexCount;
		if(ThisIndexCount > MaxIndexCount) MaxIndexCount = ThisIndexCount;

		// Main loop over runs within Last and This rows
		while (LastIndex < LastIndexCount && ThisIndex < ThisIndexCount)
		{
			ComputeData = 0;
		
			if(LastIndex == 0) LastStart = 0;
			else LastStart = Transition[LastOffset + LastIndex - 1];
			LastEnd = Transition[LastOffset + LastIndex] - 1;
			LastColor = LastIndex - 2 * (LastIndex / 2);
			LastRegionNum = LastRegion[LastIndex];

			regionDataLastRegion = RegionData[LastRegionNum];

			
			if(ThisIndex == 0) ThisStart = 0;
			else ThisStart = Transition[ThisOffset + ThisIndex - 1];
			ThisEnd = Transition[ThisOffset + ThisIndex] - 1;
			ThisColor = ThisIndex - 2 * (ThisIndex / 2);
			ThisRegionNum = ThisRegion[ThisIndex];

			if( ThisRegionNum >= 0 )
				regionDataThisRegion = RegionData[ThisRegionNum];
			else
				regionDataThisRegion = NULL;


			// blobs externs
			CandidatExterior = false;
			if( 
#if !IMATGE_CICLICA_VERTICAL
				ThisRow == 1 || ThisRow == Rows ||
#endif
#if !IMATGE_CICLICA_HORITZONTAL
				ThisStart <= 1 || ThisEnd >= Cols || 
#endif				
				GetExternPerimeter( ThisStart, ThisEnd, ThisRow, inputImage->width, inputImage->height, imatgePerimetreExtern )
				)
			{
				CandidatExterior = true;
			}
			
			int TestA = (LastEnd < ThisStart - 1);	// initially false
			int TestB = (ThisEnd < LastStart);		// initially false
			int TestC = (LastStart < ThisStart);	// initially false
			int TestD = (ThisEnd < LastEnd);
			int TestE = (ThisEnd == LastEnd);

			int TestMatch = (ThisColor == LastColor);		// initially true
			int TestKnown = (ThisRegion[ThisIndex] >= 0);	// initially false

			int Case = 0;
			if(TestA) Case = 1;
			else if(TestB) Case = 8;
			else if(TestC)
			{
				if(TestD) Case = 3;
				else if(!TestE) Case = 2;
				else Case = 4;
			}
			else
			{
				if(TestE) Case = 5;
				else if(TestD) Case = 7;
				else Case = 6;
			}

			// Initialize common variables
			ThisArea = (float) 0.0;

			if(findmoments)
			{
				ThisSumX = ThisSumY = (float) 0.0;
				ThisSumXX = ThisSumYY = ThisSumXY = (float) 0.0;
			}
			ThisMinX = ThisMinY = (float) 1000000.0;
			ThisMaxX = ThisMaxY = (float) -1.0;

			LastPerimeter = ThisPerimeter = (float) 0.0;
			ThisParent = (float) -1;
			ThisExternPerimeter = 0.0;

			// Determine necessary action and take it
			switch (Case)
			{ 
				case 1: //|xxx    |
						//|    yyy|
					
					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					LastIndex++;
					
					//afegim la cantonada a LastRegion
					actualedge.x = ThisEnd;
					actualedge.y = ThisRow - 1;
					cvSeqPush(regionDataLastRegion->edges,&actualedge);

					//afegim la cantonada a ThisRegion
					actualedge.x = ThisStart - 1;
					actualedge.y = ThisRow - 1;
					cvSeqPush(regionDataThisRegion->edges,&actualedge);
					
					break;
					
					
				case 2: //|xxxxoo |
						//|    yyy|

					if(TestMatch)	// Same color
					{
						ThisRegionNum = LastRegionNum;
						regionDataThisRegion = regionDataLastRegion;

						ThisArea = ThisEnd - ThisStart + 1;
						LastPerimeter = LastEnd - ThisStart + 1;	// to subtract
						ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter +
										PERIMETRE_DIAGONAL*2;

						if( CandidatExterior )
						{
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );
							ThisExternPerimeter += PERIMETRE_DIAGONAL*2;
						}
						ComputeData = 1;
					}

					//afegim la cantonada a ThisRegion
					if(ThisRegionNum!=-1)
					{
						// afegim dos vertexs si són diferents, només
						if(ThisStart - 1 != ThisEnd)
						{
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataThisRegion->edges,&actualedge);
						}
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
					}
					//afegim la cantonada a ThisRegion
					if(LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
						// afegim dos vertexs si són diferents, només
						if(ThisStart - 1 != ThisEnd)
						{
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataLastRegion->edges,&actualedge);
						}
					}

					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					LastIndex++;
					break;
					
					
				case 3: //|xxxxxxx|
						//|  yyyy |

					if(TestMatch)	// Same color
					{
						ThisRegionNum = LastRegionNum;
						regionDataThisRegion = regionDataLastRegion;

						ThisArea = ThisEnd - ThisStart + 1;
						LastPerimeter = ThisArea;	// to subtract
						ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL*2;
						if( CandidatExterior )
						{
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

							ThisExternPerimeter += PERIMETRE_DIAGONAL * 2;
						}
					}
					else		// Different color => New region
					{
						ThisParent = LastRegionNum;
						ThisRegionNum = ++HighRegionNum;
						ThisArea = ThisEnd - ThisStart + 1;
						ThisPerimeter = 2 + 2 * ThisArea;
						RegionData.push_back( new CBlob() );
						regionDataThisRegion = RegionData.back();

						SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum);
						if( CandidatExterior )
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

					}

					if(ThisRegionNum!=-1)
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisStart - 1;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
						//afegim la cantonada a la regio
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
					}
					// si hem creat un nou blob, afegim tb a l'anterior
					if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisStart - 1;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
						//afegim la cantonada a la regio
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
					}
					
					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					ComputeData = 1;
					ThisIndex++;
					break;
					
					
				case 4:	//|xxxxxxx|
						//|  yyyyy|
					
					if(TestMatch)	// Same color
					{
						ThisRegionNum = LastRegionNum;
						regionDataThisRegion = regionDataLastRegion;
						ThisArea = ThisEnd - ThisStart + 1;
						LastPerimeter = ThisArea;	// to subtract
						ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL;
						if( CandidatExterior )
						{
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

							ThisExternPerimeter += PERIMETRE_DIAGONAL;
						}
					}
					else		// Different color => New region
					{
						ThisParent = LastRegionNum;
						ThisRegionNum = ++HighRegionNum;
						ThisArea = ThisEnd - ThisStart + 1;
						ThisPerimeter = 2 + 2 * ThisArea;
						RegionData.push_back( new CBlob() );
						regionDataThisRegion = RegionData.back();
						SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum);
						if( CandidatExterior )
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

					}
					
					if(ThisRegionNum!=-1)
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisStart - 1;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
					}
					// si hem creat un nou blob, afegim tb a l'anterior
					if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
						actualedge.x = ThisStart - 1;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
					}
					
					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					ComputeData = 1;
					
#ifdef B_CONNECTIVITAT_8
					if( TestMatch )
					{
						LastIndex++;
						ThisIndex++;
					}
					else
					{
						LastIndex++;	
					}
#else
					LastIndex++;
					ThisIndex++;
#endif					
					break;
					
					
				case 5:	//|ooxxxxx|
						//|yyyyyyy|

					if(!TestMatch && !TestKnown)	// Different color and unknown => new region
					{
						ThisParent = LastRegionNum;
						ThisRegionNum = ++HighRegionNum;
						ThisArea = ThisEnd - ThisStart + 1;
						ThisPerimeter = 2 + 2 * ThisArea;
						RegionData.push_back( new CBlob() );
						regionDataThisRegion = RegionData.back();
						SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum);
						if( CandidatExterior )
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

					}
					else if(TestMatch && !TestKnown)	// Same color and unknown
					{
						ThisRegionNum = LastRegionNum;
						regionDataThisRegion = regionDataLastRegion;
						ThisArea = ThisEnd - ThisStart + 1;
						LastPerimeter = LastEnd - LastStart + 1;	// to subtract
						ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter 
										+ PERIMETRE_DIAGONAL * (LastStart != ThisStart);
						if( CandidatExterior )
						{
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );


							ThisExternPerimeter += PERIMETRE_DIAGONAL * (LastStart != ThisStart);
						}
						ComputeData = 1;
					}
					else if(TestMatch && TestKnown)	// Same color and known
					{
						LastPerimeter = LastEnd - LastStart + 1;	// to subtract
						//ThisPerimeter = - LastPerimeter;
						ThisPerimeter = - 2 * LastPerimeter 
										+ PERIMETRE_DIAGONAL * (LastStart != ThisStart);
						
						if(ThisRegionNum > LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, 
									findmoments, ThisRegionNum, LastRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum;
								if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum;
							}					
							ThisRegionNum = LastRegionNum;
						}
						else if(ThisRegionNum < LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, 
									findmoments, LastRegionNum, ThisRegionNum );

							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum;
								if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum;
							}					
							LastRegionNum = ThisRegionNum;
						}
					}

					
					if(ThisRegionNum!=-1)
					{
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);

						if( ThisStart - 1 != LastEnd )
						{
							//afegim la cantonada a la regio
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataThisRegion->edges,&actualedge);
						}
					}
					// si hem creat un nou blob, afegim tb a l'anterior
					if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
					}

					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					
#ifdef B_CONNECTIVITAT_8
					if( TestMatch )
					{
						LastIndex++;
						ThisIndex++;
					}
					else
					{
						LastIndex++;	
					}
#else
					LastIndex++;
					ThisIndex++;
#endif	
					break;
					
					
				case 6:	//|ooxxx  |
						//|yyyyyyy|

					if(TestMatch && !TestKnown)
					{
						ThisRegionNum = LastRegionNum;
						regionDataThisRegion = regionDataLastRegion;
						ThisArea = ThisEnd - ThisStart + 1;
						LastPerimeter = LastEnd - LastStart + 1;	// to subtract
						ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter
										+ PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart);
						if( CandidatExterior )
						{
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );


							ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart);
						}
						ComputeData = 1;
					}
					else if(TestMatch && TestKnown)
					{
						LastPerimeter = LastEnd - LastStart + 1;	// to subtract
						//ThisPerimeter = - LastPerimeter;
						ThisPerimeter = - 2 * LastPerimeter
										+ PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart);
						
						if(ThisRegionNum > LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, 
									findmoments, ThisRegionNum, LastRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum;
								if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum;
							}					
							ThisRegionNum = LastRegionNum;
						}
						else if(ThisRegionNum < LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, 
									findmoments, LastRegionNum, ThisRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum;
								if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum;
							}					
							LastRegionNum = ThisRegionNum;
						}
					}

					
					if(ThisRegionNum!=-1)
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
						if( ThisStart - 1 != LastEnd )
						{
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataThisRegion->edges,&actualedge);
						}
					}
					// si hem creat un nou blob, afegim tb a l'anterior
					if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
						//afegim la cantonada a la regio
						if( ThisStart - 1 != LastEnd )
						{
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataThisRegion->edges,&actualedge);
						}
					}					

					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					LastIndex++;
					break;
					
					
				case 7:	//|ooxxxxx|
						//|yyyy   |

					if(!TestMatch && !TestKnown)	// Different color and unknown => new region
					{
						ThisParent = LastRegionNum;
						ThisRegionNum = ++HighRegionNum;
						ThisArea = ThisEnd - ThisStart + 1;
						ThisPerimeter = 2 + 2 * ThisArea;
						RegionData.push_back( new CBlob() );
						regionDataThisRegion = RegionData.back();
						SubsumedRegion = NewSubsume(SubsumedRegion,HighRegionNum);
						if( CandidatExterior )
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

					}
					else if(TestMatch && !TestKnown)
					{
						ThisRegionNum = LastRegionNum;
						regionDataThisRegion = regionDataLastRegion;
						ThisArea = ThisEnd - ThisStart + 1;
						ThisPerimeter = 2 + ThisArea;
						LastPerimeter = ThisEnd - LastStart + 1;
						ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter
										+ PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart);
						if( CandidatExterior )
						{
							ThisExternPerimeter = GetExternPerimeter( ThisStart, ThisEnd, ThisRow, 
																	  inputImage->width, inputImage->height, 
																	  imatgePerimetreExtern );

							ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart);
						}
						ComputeData = 1;
					}
					else if(TestMatch && TestKnown)
					{
						LastPerimeter = ThisEnd - LastStart + 1;	// to subtract
						//ThisPerimeter = - LastPerimeter;
						ThisPerimeter = - 2 * LastPerimeter
										+ PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart!=LastStart);
						
						if(ThisRegionNum > LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, 
									findmoments, ThisRegionNum, LastRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum;
								if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum;
							}					
							ThisRegionNum = LastRegionNum;
						}
						else if(ThisRegionNum < LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, 
									findmoments, LastRegionNum, ThisRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum;
								if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum;
							}					
							LastRegionNum = ThisRegionNum;
						}
					}

					if(ThisRegionNum!=-1)
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
						if( ThisStart - 1 != LastEnd )
						{
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataThisRegion->edges,&actualedge);
						}
					}
					// si hem creat un nou blob, afegim tb a l'anterior
					if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisEnd;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
						if( ThisStart - 1 != LastEnd )
						{
							actualedge.x = ThisStart - 1;
							actualedge.y = ThisRow - 1;
							cvSeqPush(regionDataThisRegion->edges,&actualedge);
						}
					}

					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					ThisIndex++;
					break;
					
				case 8:	//|    xxx|
						//|yyyy   |
					
#ifdef B_CONNECTIVITAT_8					
					// fusionem blobs
					if( TestMatch )
					{
						if(ThisRegionNum > LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, 
									findmoments, ThisRegionNum, LastRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum;
								if(LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum;
							}					
							ThisRegionNum = LastRegionNum;
						}
						else if(ThisRegionNum < LastRegionNum)
						{
							Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, 
									findmoments, LastRegionNum, ThisRegionNum );
							for(int iOld = 0; iOld < MaxIndexCount; iOld++)
							{
								if(ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum;
								if(LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum;
							}					
							LastRegionNum = ThisRegionNum;
						}

						regionDataThisRegion->perimeter = regionDataThisRegion->perimeter + PERIMETRE_DIAGONAL*2;
					}
#endif

					if(ThisRegionNum!=-1)
					{
						//afegim la cantonada a la regio
						actualedge.x = ThisStart - 1;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataThisRegion->edges,&actualedge);
					}
#ifdef B_CONNECTIVITAT_8					
					// si hem creat un nou blob, afegim tb a l'anterior
					if(!TestMatch && LastRegionNum!=-1 && LastRegionNum != ThisRegionNum )
					{
#endif					
						//afegim la cantonada a la regio
						actualedge.x = ThisStart - 1;
						actualedge.y = ThisRow - 1;
						cvSeqPush(regionDataLastRegion->edges,&actualedge);
#ifdef B_CONNECTIVITAT_8
					}
#endif

					ThisRegion[ThisIndex] = ThisRegionNum;
					LastRegion[LastIndex] = LastRegionNum;
					ThisIndex++;
#ifdef B_CONNECTIVITAT_8					
					LastIndex--;
#endif
					break;
					
				default:
					ErrorFlag = -1;
			}	// end switch case

			// calculate the blob moments and mean gray level of the current blob (ThisRegionNum)
			if(ComputeData > 0)
			{
				// compute blob moments if necessary
				if(findmoments)
				{
					float ImageRow = (float) (ThisRow - 1);

					for(int k = ThisStart; k <= ThisEnd; k++)
					{
						ThisSumX += (float) (k - 1);
						ThisSumXX += (float) (k - 1) * (k - 1);
					}
					
					ThisSumXY = ThisSumX * ImageRow;
					ThisSumY = ThisArea * ImageRow;
					ThisSumYY = ThisSumY * ImageRow;
					
				}

				// compute the mean gray level and its std deviation 
				if(ThisRow <= Rows )
				{
					pImageAux = pImage + ThisStart;
					if(maskImage!=NULL) pMaskAux = pMask + ThisStart;
					for(int k = ThisStart; k <= ThisEnd; k++)
					{
						if((k>0) && (k <= Cols))
						{
							if( maskImage!= NULL)
							{
								// només es té en compte el valor del píxel de la
								// imatge que queda dins de la màscara
								// (de pas, comptem el nombre de píxels de la màscara)
								if( ((unsigned char) *pMaskAux) != PIXEL_EXTERIOR )
								{
									imagevalue = (unsigned char) (*pImageAux);
									regionDataThisRegion->mean+=imagevalue;
									regionDataThisRegion->stddev+=imagevalue*imagevalue;
								}
								else
								{
									nombre_pixels_mascara++;
								}
							}
							else
							{
								imagevalue = (unsigned char) (*pImageAux);
								regionDataThisRegion->mean+=imagevalue;
								regionDataThisRegion->stddev+=imagevalue*imagevalue;

							}
						}
						pImageAux++;
						if(maskImage!=NULL) pMaskAux++;
					}
				}

				// compute the min and max values of X and Y
				if(ThisStart - 1 < (int) ThisMinX) ThisMinX = (float) (ThisStart - 1);
				if(ThisMinX < (float) 0.0) ThisMinX = (float) 0.0;
				if(ThisEnd > (int) ThisMaxX) ThisMaxX = (float) ThisEnd;

				if(ThisRow - 1 < ThisMinY) ThisMinY = ThisRow - 1;
				if(ThisMinY < (float) 0.0) ThisMinY = (float) 0.0;
				if(ThisRow > ThisMaxY) ThisMaxY = ThisRow;
			}

			// put the current results into RegionData
			if(ThisRegionNum >= 0)
			{
				if(ThisParent >= 0) { regionDataThisRegion->parent = (int) ThisParent; }
				regionDataThisRegion->etiqueta = ThisRegionNum;
				regionDataThisRegion->area += ThisArea;
				regionDataThisRegion->perimeter += ThisPerimeter;
				regionDataThisRegion->externPerimeter += ThisExternPerimeter;
				
				if(ComputeData > 0)
				{
					if(findmoments)
					{
						regionDataThisRegion->sumx += ThisSumX;
						regionDataThisRegion->sumy += ThisSumY;
						regionDataThisRegion->sumxx += ThisSumXX;
						regionDataThisRegion->sumyy += ThisSumYY;
						regionDataThisRegion->sumxy += ThisSumXY;
					}
					regionDataThisRegion->perimeter -= LastPerimeter;
					regionDataThisRegion->minx=MIN(regionDataThisRegion->minx,ThisMinX);
					regionDataThisRegion->maxx=MAX(regionDataThisRegion->maxx,ThisMaxX);
					regionDataThisRegion->miny=MIN(regionDataThisRegion->miny,ThisMinY);
					regionDataThisRegion->maxy=MAX(regionDataThisRegion->maxy,ThisMaxY);
				}
				// blobs externs
				if( CandidatExterior )
				{
					regionDataThisRegion->exterior = true;
				}
			
			}
		}	// end Main loop

		if(ErrorFlag != 0) return false;
		// ens situem al primer pixel de la seguent fila
		pImage = inputImage->imageData - 1 + startCol + (ThisRow+startRow) * inputImage->widthStep;

		if(maskImage!=NULL)
			pMask = maskImage->imageData - 1 + ThisRow * maskImage->widthStep;
	}	// end Loop over all rows

	// eliminem l'àrea del marc
	// i també els píxels de la màscara
	// ATENCIO: PERFER: el fet de restar el nombre_pixels_mascara del
	// blob 0 només serà cert si la màscara té contacte amb el marc.
	// Si no, s'haurà de trobar quin és el blob que conté més píxels del
	// compte.
	RegionData[0]->area -= ( Rows + 1 + Cols + 1 )*2 + nombre_pixels_mascara;

	// eliminem el perímetre de més:
	// - sense marc: 2m+2n (perímetre extern)
	// - amb marc:   2(m+2)+2(n+2) = 2m+2n + 8
	// (segurament no és del tot acurat)
	// (i amb les màscares encara menys...)
	RegionData[0]->perimeter -= 8.0;

	blob_vector::iterator iti;
	CBlob *blobActual;

	if(findmoments)
	{
		iti = RegionData.begin();
		// Normalize summation fields into moments 
		for(ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++)
		{
			blobActual = *iti;

			if(!SubsumedRegion[ThisRegionNum])	// is a valid blob?
			{
				// Get averages
				blobActual->sumx /= blobActual->area;
				blobActual->sumy /= blobActual->area;
				blobActual->sumxx /= blobActual->area;
				blobActual->sumyy /= blobActual->area;
				blobActual->sumxy /= blobActual->area;

				// Create moments
				blobActual->sumxx -= blobActual->sumx * blobActual->sumx;
				blobActual->sumyy -= blobActual->sumy * blobActual->sumy;
				blobActual->sumxy -= blobActual->sumx * blobActual->sumy;
				if(blobActual->sumxy > -1.0E-14 && blobActual->sumxy < 1.0E-14)
				{
					blobActual->sumxy = (float) 0.0; // Eliminate roundoff error
				}
			}			
		}
	}

	//Get the real mean and std deviation
	iti = RegionData.begin();
	for(ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++)
	{
		blobActual = *iti;
		if(!SubsumedRegion[ThisRegionNum])	// is a valid blob?
		{
			if(blobActual->area > 1)
			{
				blobActual->stddev =
									sqrt(
									(
									blobActual->stddev * blobActual->area -
									blobActual->mean * blobActual->mean 
									)/
									(blobActual->area*(blobActual->area-1))
									);
			}
			else
				blobActual->stddev=0;
			
			if(blobActual->area > 0)
				blobActual->mean/=blobActual->area;
			else
				blobActual->mean = 0;
		}
	
	}

	// eliminem els blobs subsumats
	//blob_vector::iterator itBlobs = RegionData.begin() + HighRegionNum + 1;
	blob_vector::iterator itBlobs = RegionData.begin();

	ThisRegionNum = 0;
	while( itBlobs != RegionData.end() )
	{
		if(SubsumedRegion[ThisRegionNum])	// is not a valid blob?
		{
			delete *itBlobs;
			itBlobs = RegionData.erase( itBlobs );
		}
		else		
			itBlobs++;

		ThisRegionNum++;
	}

	free(SubsumedRegion);
	delete Transition;
	delete ThisRegion;
	delete LastRegion;

	if( imatgePerimetreExtern ) cvReleaseImage(&imatgePerimetreExtern);

	return true;
}


int *NewSubsume(int *subsumed, int index_subsume)
{
	if( index_subsume == 0 )
	{
		subsumed = (int*)malloc(sizeof(int));
	}
	else
	{
		subsumed = (int*)realloc(subsumed,(index_subsume+1)*sizeof(int));
	}
	subsumed[index_subsume]=0;
	return subsumed;
}

/**
 Fusiona dos blobs i afegeix el blob les característiques del blob RegionData[HiNum]
 al blob RegionData[LoNum]. Al final allibera el blob de RegionData[HiNum]
*/
void Subsume(blob_vector &RegionData,
			int HighRegionNum,
			int* SubsumedRegion,
			CBlob* blobHi,
			CBlob* blobLo,
			bool findmoments,
			int HiNum,
			int LoNum)
{
	// cout << "\nSubsuming " << HiNum << " into " << LoNum << endl; // for debugging

	int i;
	
	blobLo->minx=MIN(blobHi->minx,blobLo->minx);
	blobLo->miny=MIN(blobHi->miny,blobLo->miny);
	blobLo->maxx=MAX(blobHi->maxx,blobLo->maxx);
	blobLo->maxy=MAX(blobHi->maxy,blobLo->maxy);
	blobLo->area+=blobHi->area;	
	blobLo->perimeter+=blobHi->perimeter;
	blobLo->externPerimeter += blobHi->externPerimeter;
	blobLo->exterior = blobLo->exterior || blobHi->exterior;
	blobLo->mean += blobHi->mean;
	blobLo->stddev += blobHi->stddev;	
	
	if( findmoments )
	{
		blobLo->sumx+=blobHi->sumx;	
		blobLo->sumy+=blobHi->sumy;	
		blobLo->sumxx+=blobHi->sumxx;	
		blobLo->sumyy+=blobHi->sumyy;	
		blobLo->sumxy+=blobHi->sumxy;
	}
	// Make sure no region still has subsumed region as parent
	blob_vector::iterator it = (RegionData.begin() + HiNum + 1);

	for(i = HiNum + 1; i <= HighRegionNum; i++, it++)
	{
		if((*it)->parent == (float) HiNum) { (*it)->parent = LoNum; }
	}

	// Mark dead region number for future compression
	SubsumedRegion[HiNum] = 1;
	// marquem el blob com a lliure
	blobHi->etiqueta=-1;

	// Atenció!!!! abans d'eliminar els edges 
	// s'han de traspassar del blob HiNum al blob LoNum
	blobHi->CopyEdges( *blobLo );
	blobHi->ClearEdges();
}

/**
- FUNCIÓ: GetExternPerimeter
- FUNCIONALITAT: Retorna el perimetre extern d'una run lenght
- PARÀMETRES:
	- start: columna d'inici del run
	- end: columna final del run
	- row: fila del run
	- maskImage: màscara pels pixels externs
- RESULTAT:
	- quantitat de perimetre extern d'un run, suposant que és un blob 
	  d'una única fila d'alçada
- RESTRICCIONS:
- AUTOR: 
- DATA DE CREACIÓ: 2006/02/27
- MODIFICACIÓ: Data. Autor. Descripció.
*/
double GetExternPerimeter( int start, int end, int row, int width, int height, IplImage *imatgePerimetreExtern )
{
	double perimeter = 0.0f;
	char *pPerimetre;


	// comprovem les dimensions de la imatge
	perimeter += ( start <= 0 ) + ( end >= width - 1 );
	if(row <= 1 ) perimeter+= start - end;
	if(row >= height - 1 ) perimeter+= start - end;


	// comprovem els pixels que toquen a la màscara (si s'escau)
	if( imatgePerimetreExtern != NULL )
	{
		if( row <= 0 || row >= height ) return perimeter;

		if( start < 0 ) start = 1;
		if( end >= width ) end = width - 2;
		
		pPerimetre = imatgePerimetreExtern->imageData + (row - 1) * imatgePerimetreExtern->widthStep + start;
		for (int x = start - 1; x <= end; x++ )
		{
			perimeter += *pPerimetre;
			pPerimetre++;
		}
	}
	
	return perimeter;
}