org.bytedeco.javacv.Blobs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javacv Show documentation
Show all versions of javacv Show documentation
Java interface to OpenCV, FFmpeg, and more
package org.bytedeco.javacv;
import org.bytedeco.opencv.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_core.*;
//***************************************************************//
//* Blob analysis package Version3.0 3 Oct 2012 *//
//* - Version 1.0: 8 Aug 2003 *//
//* - Version 1.2: 3 Jan 2008 *//
//* - Version 1.3: 5 Jan 2008 Add BLOBCOLOR *//
//* - Version 1.4: 13 January 2008 Add ROI function *//
//* - Version 1.5: 13 April 2008 Fix perimeter on Region 0 *//
//* - Version 1.6: 1 May 2008 Reduce size of working storage *//
//* - Version 1.7: 2 May 2008 Speed up run code initialization *//
//* - Version 1.8: 4 May 2008 Fix bugs in perimeter & Reg 0 *//
//* - Version 2.0: 3 Jan 2009 Add labeling functionality *//
//* - Version 3.0: 3 Oct 2012 Convert to Java *//
//* - Eliminate labeling functionality (but it's still there) *//
//* - Simplify (at slight expense of performance) *//
//* - Reduce to 4 connectivity *//
//* *//
//* Input: IplImage binary image *//
//* Output: attributes of each connected region *//
//* Internal data: labeled array (could easily be externalized) *//
//* Author: Dave Grossman *//
//* Email: [email protected] *//
//* Acknowledgement: my code is based on an algorithm that was *//
//* to the best of my knowledge originally developed by Gerry *//
//* Agin of SRI around the year 1973. I have not been able to *//
//* find any published references to his earlier work. I posted *//
//* early versions of my program to OpenCV, where they morphed *//
//* eventually into cvBlobsLib. *//
//* *//
//* As the author of this code, I place all of this code into *//
//* the public domain. Users can use it for any legal purpose. *//
//* *//
//* - Dave Grossman *//
//* *//
//* Typical calling sequence: *//
//* Blobs Blob = new Blobs(); *//
//* Blob.BlobAnalysis( *//
//* image3, // image *//
//* -1, -1, // ROI start col, row (-1 means full image) *//
//* -1, -1, // ROI cols, rows *//
//* 0, // border (0 = black; 1 = white) *//
//* 20); // minarea *//
//* Blob.PrintRegionData(); *//
//* int BlobLabel = Blob.NextRegion( *//
//* -1, // parentcolor (-1 = ignore) *//
//* 0, // color (0 = black; 1 = white; -1 = ignore *//
//* 100, // minarea *//
//* 500, // maxarea *//
//* 15); // starting label (default 0) *//
//* *//
//* Ellipse properties can be derived from moments: *//
//* h = (XX + YY) / 2 *//
//* Major axis = h + sqrt ( h^2 - XX * YY + XY^2) *//
//* Minor axis = h - sqrt ( h^2 - XX * YY2 + XY^2) *//
//* Eccentricity = (sqrt(abs(XX - YY)) + 4 * XY)/AREA *//
//***************************************************************//
public class Blobs
{
// The following parameters should be configured by the user:
// On ScanSnap Manager, "Best" setting = 300dpi gray level
// jpg compression is set to minimum so that quality is highest
// Each page jpg image is then a little under 1 MB
static int BLOBROWCOUNT = 3500; // 11 inches * 8.5 inches standard page
static int BLOBCOLCOUNT = 2700; // with some added cushion to be safe
// Allow for vast number of blobs so there is no memory overrun
static int BLOBTOTALCOUNT = (BLOBROWCOUNT + BLOBCOLCOUNT) * 5;
//--------------------------------------------------------------
// Do not change anything below this line
public static int BLOBLABEL = 0;
public static int BLOBPARENT = 1;
public static int BLOBCOLOR = 2;
public static int BLOBAREA = 3;
public static int BLOBPERIMETER = 4;
public static int BLOBSUMX = 5;
public static int BLOBSUMY = 6;
public static int BLOBSUMXX = 7;
public static int BLOBSUMYY = 8;
public static int BLOBSUMXY = 9;
public static int BLOBMINX = 10;
public static int BLOBMAXX = 11;
public static int BLOBMINY = 12;
public static int BLOBMAXY = 13;
public static int BLOBDATACOUNT = 14;
public static int [][] LabelMat = new int [BLOBROWCOUNT][BLOBCOLCOUNT];
public static double [][] RegionData = new double [BLOBTOTALCOUNT][BLOBDATACOUNT];
public static int MaxLabel;
public int LabelA, LabelB, LabelC, LabelD;
public int ColorA, ColorB, ColorC, ColorD;
public int jrow, jcol; // index within ROI
public static int [] SubsumedLabel = new int [BLOBTOTALCOUNT];
public static int [] CondensationMap = new int [BLOBTOTALCOUNT];
// Print out all the data for all the regions (blobs)
public void PrintRegionData() { PrintRegionData(0, MaxLabel); }
public void PrintRegionData(int Label0, int Label1)
{
if(Label0 < 0) Label0 = 0;
if(Label1 > MaxLabel) Label1 = MaxLabel;
if(Label1 < Label0) return;
for(int Label = Label0; Label <= Label1; Label++)
{
double [] Property = RegionData[Label];
int ThisLabel = (int)Property[BLOBLABEL];
int ThisParent = (int)Property[BLOBPARENT];
int ThisColor = (int)Property[BLOBCOLOR];
double ThisArea = Property[BLOBAREA];
double ThisPerimeter = Property[BLOBPERIMETER];
double ThisSumX = Property[BLOBSUMX];
double ThisSumY = Property[BLOBSUMY];
double ThisSumXX = Property[BLOBSUMXX];
double ThisSumYY = Property[BLOBSUMYY];
double ThisSumXY = Property[BLOBSUMXY];
int ThisMinX = (int)Property[BLOBMINX];
int ThisMaxX = (int)Property[BLOBMAXX];
int ThisMinY = (int)Property[BLOBMINY];
int ThisMaxY = (int)Property[BLOBMAXY];
String Str1 = " " + Label + ": L[" + ThisLabel + "] P[" + ThisParent + "] C[" + ThisColor + "]";
String Str2 = " AP[" + ThisArea + ", " + ThisPerimeter + "]";
String Str3 = " M1[" + ThisSumX + ", " + ThisSumY + "] M2[" + ThisSumXX + ", " + ThisSumYY + ", " + ThisSumXY + "]";
String Str4 = " MINMAX[" + ThisMinX + ", " + ThisMaxX + ", " + ThisMinY + ", " + ThisMaxY + "]";
String Str = Str1 + Str2 + Str3 + Str4;
System.out.println(Str);
}
System.out.println();
}
// Determine the next (higher number) region that meets the desired conditions
public static int NextRegion(int Parent, int Color, double MinArea, double MaxArea, int Label)
{
double DParent = (double) Parent;
double DColor = (double) Color; if(DColor > 0) DColor = 1;
int i;
for(i = Label; i <= MaxLabel; i++)
{
double [] Region = RegionData[i];
double ThisParent = Region[BLOBPARENT];
double ThisColor = Region[BLOBCOLOR];
if(DParent >= 0 && DParent != ThisParent) continue;
if(DColor >= 0 && DColor != ThisColor) continue;
if(Region[BLOBAREA] < MinArea || Region[BLOBAREA] > MaxArea) continue;
break; // We have a match!
}
if(i > MaxLabel) i = -1; // Use -1 to flag that there was no match
return i;
}
// Determine the prior (lower number) region that meets the desired conditions
public static int PriorRegion(int Parent, int Color, double MinArea, double MaxArea, int Label)
{
double DParent = (double) Parent;
double DColor = (double) Color; if(DColor > 0) DColor = 1;
int i;
for(i = Label; i >= 0; i--)
{
double [] Region = RegionData[i];
double ThisParent = Region[BLOBPARENT];
double ThisColor = Region[BLOBCOLOR];
if(DParent >= 0 && DParent != ThisParent) continue;
if(DColor >= 0 && DColor != ThisColor) continue;
if(Region[BLOBAREA] < MinArea || Region[BLOBAREA] > MaxArea) continue;
break; // We have a match!
}
if(i < 0) i = -1; // Use -1 to flag that there was no match
return i;
}
public void ResetRegion(int Label)
{
double [] RegionD = RegionData[Label];
RegionD[BLOBLABEL] =
RegionD[BLOBPARENT] =
RegionD[BLOBCOLOR] =
RegionD[BLOBAREA] =
RegionD[BLOBPERIMETER] =
RegionD[BLOBSUMX] =
RegionD[BLOBSUMY] =
RegionD[BLOBSUMXX] =
RegionD[BLOBSUMYY] =
RegionD[BLOBSUMXY] =
RegionD[BLOBMINX] =
RegionD[BLOBMAXX] =
RegionD[BLOBMINY] =
RegionD[BLOBMAXY] = 0.0;
System.arraycopy(RegionD,0,RegionData[Label],0,BLOBDATACOUNT); // RegionData[Label] <- RegionD;
}
public void OldRegion(
int NewLabelD, // 3rd update this (may be the same as Label1 or Label2)
int Label1, // 1st increment this by 1
int Label2) // 2nd increment this by 1
{
int DeltaPerimeter = 0;
if(Label1 >= 0 && Label1 != NewLabelD)
{
DeltaPerimeter++;
double [] Region1 = RegionData[Label1];
Region1[BLOBPERIMETER]++;
System.arraycopy(Region1,0,RegionData[Label1],0,BLOBDATACOUNT); // RegionData[Label1] <- Region1;
}
if(Label2 >= 0 && Label2 != NewLabelD)
{
DeltaPerimeter++;
double [] Region2 = RegionData[Label2];
Region2[BLOBPERIMETER]++;
System.arraycopy(Region2,0,RegionData[Label2],0,BLOBDATACOUNT); // RegionData[Label2] <- Region2;
}
LabelD = NewLabelD;
double [] RegionD = RegionData[LabelD];
RegionD[BLOBLABEL] = LabelD;
RegionD[BLOBPARENT] += 0.0; // no change
RegionD[BLOBCOLOR] += 0.0; // no change
RegionD[BLOBAREA] += 1.0;
RegionD[BLOBPERIMETER] += DeltaPerimeter;
RegionD[BLOBSUMX] += jcol;
RegionD[BLOBSUMY] += jrow;
RegionD[BLOBSUMXX] += jcol*jcol;
RegionD[BLOBSUMYY] += jrow*jrow;
RegionD[BLOBSUMXY] += jcol*jrow;
RegionD[BLOBMINX] = Math.min(RegionD[BLOBMINX], jcol);
RegionD[BLOBMAXX] = Math.max(RegionD[BLOBMAXX], jcol);
RegionD[BLOBMINY] = Math.min(RegionD[BLOBMINY], jrow);
RegionD[BLOBMAXY] = Math.max(RegionD[BLOBMAXY], jrow);
System.arraycopy(RegionD,0,RegionData[LabelD],0,BLOBDATACOUNT); // RegionData[LabelD] <- RegionD;
}
public void NewRegion(int ParentLabel)
{
LabelD = ++MaxLabel;
double [] RegionD = RegionData[LabelD];
RegionD[BLOBLABEL] = LabelD;
RegionD[BLOBPARENT] = (double) ParentLabel;
RegionD[BLOBCOLOR] = ColorD;
RegionD[BLOBAREA] = 1.0;
RegionD[BLOBPERIMETER] = 2.0;
RegionD[BLOBSUMX] = jcol;
RegionD[BLOBSUMY] = jrow;
RegionD[BLOBSUMXX] = jcol*jcol;
RegionD[BLOBSUMYY] = jrow*jrow;
RegionD[BLOBSUMXY] = jcol*jrow;
RegionD[BLOBMINX] = jcol;
RegionD[BLOBMAXX] = jcol;
RegionD[BLOBMINY] = jrow;
RegionD[BLOBMAXY] = jrow;
System.arraycopy(RegionD,0,RegionData[LabelD],0,BLOBDATACOUNT); // RegionData[LabelD] <- RegionD;
SubsumedLabel[LabelD] = -1; // Flag label as not subsumed
double [] RegionB = RegionData[LabelB];
RegionB[BLOBPERIMETER]++;
System.arraycopy(RegionB,0,RegionData[LabelB],0,BLOBDATACOUNT); // RegionData[LabelB] <- RegionB;
double [] RegionC = RegionData[LabelC];
RegionC[BLOBPERIMETER]++;
System.arraycopy(RegionC,0,RegionData[LabelC],0,BLOBDATACOUNT); // RegionData[LabelC] <- RegionC;
}
public void Subsume(int GoodLabel, int BadLabel, int PSign) // Combine data with parent
{
LabelD = GoodLabel;
double [] GoodRegion = RegionData[GoodLabel];
double [] BadRegion = RegionData[BadLabel];
GoodRegion[BLOBLABEL] = GoodRegion[BLOBLABEL]; // no change
GoodRegion[BLOBPARENT] = GoodRegion[BLOBPARENT]; // no change
GoodRegion[BLOBCOLOR] = GoodRegion[BLOBCOLOR]; // no change
GoodRegion[BLOBAREA] += BadRegion[BLOBAREA];
GoodRegion[BLOBPERIMETER] += BadRegion[BLOBPERIMETER] * PSign; // + external or - internal perimeter
GoodRegion[BLOBSUMX] += BadRegion[BLOBSUMX];
GoodRegion[BLOBSUMY] += BadRegion[BLOBSUMY];
GoodRegion[BLOBSUMXX] += BadRegion[BLOBSUMXX];
GoodRegion[BLOBSUMYY] += BadRegion[BLOBSUMYY];
GoodRegion[BLOBSUMXY] += BadRegion[BLOBSUMXY];
GoodRegion[BLOBMINX] = Math.min(GoodRegion[BLOBMINX], BadRegion[BLOBMINX]);
GoodRegion[BLOBMAXX] = Math.max(GoodRegion[BLOBMAXX], BadRegion[BLOBMAXX]);
GoodRegion[BLOBMINY] = Math.min(GoodRegion[BLOBMINY], BadRegion[BLOBMINY]);
GoodRegion[BLOBMAXY] = Math.max(GoodRegion[BLOBMAXY], BadRegion[BLOBMAXY]);
System.arraycopy(GoodRegion,0,RegionData[GoodLabel],0,BLOBDATACOUNT); // RegionData[GoodLabel] <- GoodRegion;
}
public static int SubsumptionChain(int x) { return SubsumptionChain(x, 0); }
public static int SubsumptionChain(int x, int Print)
{
String Str = "";
if(Print > 0) Str = "Subsumption chain for " + x + ": ";
int Lastx = x;
while(x > -1)
{
Lastx = x;
if(Print > 0) Str += " " + x;
if(x == 0) break;
x = SubsumedLabel[x];
}
if(Print > 0) System.out.println(Str);
return Lastx;
}
//---------------------------------------------------------------------------------------
// Main blob analysis routine
//---------------------------------------------------------------------------------------
// RegionData[0] is the border. It has Property[BLOBPARENT] = 0.
public int BlobAnalysis(IplImage Src, // input image
int Col0, int Row0, // start of ROI
int Cols, int Rows, // size of ROI
int Border, // border color (0 = black; 1 = white)
int MinArea) // minimum region area
{
CvMat SrcMat = Src.asCvMat();
int SrcCols = SrcMat.cols();
int SrcRows = SrcMat.rows();
if(Col0 < 0) Col0 = 0;
if(Row0 < 0) Row0 = 0;
if(Cols < 0) Cols = SrcCols;
if(Rows < 0) Rows = SrcRows;
if(Col0 + Cols > SrcCols) Cols = SrcCols - Col0;
if(Row0 + Rows > SrcRows) Rows = SrcRows - Row0;
if(Cols > BLOBCOLCOUNT || Rows > BLOBROWCOUNT )
{
System.out.println("Error in Class Blobs: Image too large: Edit Blobs.java");
System.exit(666);
return 0;
}
// Initialization
int FillLabel = 0;
int FillColor = 0; if(Border > 0) { FillColor = 1; }
LabelA = LabelB = LabelC = LabelD = 0;
ColorA = ColorB = ColorC = ColorD = FillColor;
for(int k = 0; k < BLOBTOTALCOUNT; k++) SubsumedLabel[k] = -1;
// Initialize border region
MaxLabel = 0;
double [] BorderRegion = RegionData[0];
BorderRegion[BLOBLABEL] = 0.0;
BorderRegion[BLOBPARENT] = -1.0;
BorderRegion[BLOBAREA] = Rows + Cols + 4; // Top, left, and 4 corners
BorderRegion[BLOBCOLOR] = FillColor;
BorderRegion[BLOBSUMX] = 0.5 * ( (2.0 + Cols) * (Cols - 1.0) ) - Rows - 1 ;
BorderRegion[BLOBSUMY] = 0.5 * ( (2.0 + Rows) * (Rows - 1.0) ) - Cols - 1 ;
BorderRegion[BLOBMINX] = -1;
BorderRegion[BLOBMINY] = -1;
BorderRegion[BLOBMAXX] = Cols + 1.0;
BorderRegion[BLOBMAXY] = Rows + 1.0;
System.arraycopy(BorderRegion,0,RegionData[0],0,BLOBDATACOUNT); // RegionData[0] <- BorderRegion;
// The cells are identified this way
// Last |AB|
// This |CD|
//
// With 4 connectivity, there are 8 possibilities for the cells:
// No color transition Color transition
// Case 1 2 3 4 5 6 7 8
// Last Row |pp|pp|pq|pq| |pp|pp|pq|pq|
// This Row |pP|qQ|pP|qQ| |pQ|qP|pQ|qP|
//
// Region numbers are p, q, r, x; where p<>q
// Upper case letter is the current element at column=x row=y
// Color is 0 or 1 (1 stands for 255 in the actual image)
// Note that Case 4 is complicated because it joins two regions
//--------------------------
// Case 1: Colors A=B; C=D; A=C
// Case 2: Colors A=B; C=D; A<>C
// Case 3: Colors A<>B;C=D; A=C
// Case 4: Colors A<>B;C=D; A<>C
// Case 5: Colors A=B; C<>D; A=C
// Case 6: Colors A=B; C<>D; A<>C
// Case 7: Colors A<>B;C<>D; A=C
// Case 8: Colors A<>B;C<>D; A<>C
//--------------------------
// Loop over rows of ROI. irow = Row0 is 1st row of image; irow = Row0+Row is last row of image.
for(int irow = Row0; irow < Row0+Rows; irow++) // index within Src
{
jrow = irow - Row0; // index within ROI. 0 is first row. Rows is last row.
// Loop over columns of ROI.
for(int icol = Col0; icol < Col0+Cols; icol++) // index within Src
{
jcol = icol - Col0; // index within ROI
// initialize
ColorA = ColorB = ColorC = FillColor;
LabelA = LabelB = LabelC = LabelD = 0;
ColorD = (int) SrcMat.get(jrow,jcol); // fetch color of cell
if(jrow == 0 || jcol == 0) // first column or row
{
if(jcol > 0)
{
ColorC = (int) SrcMat.get(jrow,jcol-1);
LabelC = LabelMat[jrow][jcol-1];
}
if(jrow > 0)
{
ColorB = (int) SrcMat.get(jrow-1,jcol);
LabelB = LabelMat[jrow-1][jcol];
}
}
else
{
ColorA = (int) SrcMat.get(jrow-1,jcol-1); if(ColorA > 0) ColorA = 1;
ColorB = (int) SrcMat.get(jrow-1,jcol); if(ColorB > 0) ColorB = 1;
ColorC = (int) SrcMat.get(jrow,jcol-1); if(ColorC > 0) ColorC = 1;
LabelA = LabelMat[jrow-1][jcol-1];
LabelB = LabelMat[jrow-1][jcol];
LabelC = LabelMat[jrow][jcol-1];
}
if(ColorA > 0) ColorA = 1;
if(ColorB > 0) ColorB = 1;
if(ColorC > 0) ColorC = 1;
if(ColorD > 0) ColorD = 1;
// Determine Case
int Case = 0;
if(ColorA == ColorB)
{
if(ColorC == ColorD) { if(ColorA == ColorC) Case = 1; else Case = 2; }
else { if(ColorA == ColorC) Case = 5; else Case = 6; }
}
else
{
if(ColorC == ColorD) { if(ColorA == ColorC) Case = 3; else Case = 4; }
else { if(ColorA == ColorC) Case = 7; else Case = 8; }
}
// Take appropriate action
if(Case == 1) { OldRegion(LabelC, -1, -1); }
else if(Case == 2 || Case == 3) { OldRegion(LabelC, LabelB, LabelC); }
else if(Case == 5 || Case == 8) // Isolated
{
if((jrow == Rows || jcol == Cols) && ColorD == FillColor) { OldRegion(0, -1, -1); } // attached to border region 0
else NewRegion(LabelB);
}
else if(Case == 6 || Case == 7) { OldRegion(LabelB, LabelB, LabelC); }
else // Case 4 - The complicated situation
{
int LabelBRoot = SubsumptionChain(LabelB);
int LabelCRoot = SubsumptionChain(LabelC);
int LabelRoot = Math.min(LabelBRoot, LabelCRoot);
int LabelX;
if(LabelBRoot < LabelCRoot) { OldRegion(LabelB, -1, -1); LabelX = LabelC; }
else { OldRegion(LabelC, -1, -1); LabelX = LabelB; }
int NextLabelX = LabelX;
while(LabelRoot < LabelX)
{
NextLabelX = SubsumedLabel[LabelX];
SubsumedLabel[LabelX] = LabelRoot;
LabelX = NextLabelX;
}
}
// Last column or row. Final corner was handled earlier in Cases 5 and 8.
if((jrow == Rows || jcol == Cols) && ColorD == FillColor)
{
if(jcol < Cols) // bottom row
{
if(ColorC != FillColor) // Subsume B chain to border region 0
{
int LabelRoot = SubsumptionChain(LabelB);
SubsumedLabel[LabelRoot] = 0;
}
}
else if(jrow < Rows) // right column
{
if(ColorB != FillColor) // Subsume C chain to border region 0
{
int LabelRoot = SubsumptionChain(LabelC);
SubsumedLabel[LabelRoot] = 0;
}
}
OldRegion(0, -1, -1); // attached to border region 0
}
LabelMat[jrow][jcol] = LabelD;
}
}
// Compute Condensation map
int Offset = 0;
for(int Label = 1; Label <= MaxLabel; Label++)
{
if(SubsumedLabel[Label] > -1) Offset++;
CondensationMap[Label] = Label - Offset;
}
// Subsume regions that were flagged as connected; Perimeters add
for(int Label = 1; Label <= MaxLabel; Label++)
{
int BetterLabel = SubsumptionChain(Label);
if(BetterLabel != Label) Subsume(BetterLabel, Label, 1);
}
// Condense subsumed regions
int NewMaxLabel = 0;
for(int OldLabel = 1; OldLabel <= MaxLabel; OldLabel++)
{
if(SubsumedLabel[OldLabel] < 0) // Renumber valid regions only
{
double [] OldRegion = RegionData[OldLabel];
int OldParent = (int) OldRegion[BLOBPARENT];
int NewLabel = CondensationMap[OldLabel];
int NewParent = SubsumptionChain(OldParent);
NewParent = CondensationMap[NewParent];
OldRegion[BLOBLABEL] = (double) NewLabel;
OldRegion[BLOBPARENT] = (double) NewParent;
System.arraycopy(OldRegion,0,RegionData[NewLabel],0,BLOBDATACOUNT); //RegionData[NewLabel] <- ThisRegion;
NewMaxLabel = NewLabel;
}
}
// Zero out unneeded high labels
for(int Label = NewMaxLabel+1; Label <= MaxLabel; Label++) ResetRegion(Label);
MaxLabel = NewMaxLabel;
// Flag for subsumption regions that have too small area
for(int Label = MaxLabel; Label > 0; Label--)
{
double [] ThisRegion = RegionData[Label];
int ThisArea = (int) ThisRegion[BLOBAREA];
if(ThisArea < MinArea)
{
int ThisParent = (int) ThisRegion[BLOBPARENT];
SubsumedLabel[Label] = ThisParent; // Flag this label as having been subsumed
}
else SubsumedLabel[Label] = -1;
}
// Compute Condensation map
Offset = 0;
for(int Label = 1; Label <= MaxLabel; Label++)
{
if(SubsumedLabel[Label] > -1) Offset++;
CondensationMap[Label] = Label - Offset;
}
// Subsume regions that were flagged as enclosed; Perimeters subtract
for(int Label = 1; Label <= MaxLabel; Label++)
{
int BetterLabel = SubsumptionChain(Label);
if(BetterLabel != Label) Subsume(BetterLabel, Label, -1);
}
// Condense subsumed regions
for(int OldLabel = 1; OldLabel <= MaxLabel; OldLabel++)
{
if(SubsumedLabel[OldLabel] < 0) // Renumber valid regions only
{
double [] OldRegion = RegionData[OldLabel];
int OldParent = (int) OldRegion[BLOBPARENT];
int NewLabel = CondensationMap[OldLabel];
int NewParent = SubsumptionChain(OldParent);
NewParent = CondensationMap[NewParent];
OldRegion[BLOBLABEL] = (double) NewLabel;
OldRegion[BLOBPARENT] = (double) NewParent;
System.arraycopy(OldRegion,0,RegionData[NewLabel],0,BLOBDATACOUNT); //RegionData[NewLabel] <- ThisRegion;
NewMaxLabel = NewLabel;
}
}
// Zero out unneeded high labels
for(int Label = NewMaxLabel+1; Label <= MaxLabel; Label++) ResetRegion(Label);
MaxLabel = NewMaxLabel;
// Normalize summation fields into moments
for(int Label = 0; Label <= MaxLabel; Label++)
{
double [] ThisRegion = RegionData[Label];
// Extract fields
double Area = ThisRegion[BLOBAREA];
double SumX = ThisRegion[BLOBSUMX];
double SumY = ThisRegion[BLOBSUMY];
double SumXX = ThisRegion[BLOBSUMXX];
double SumYY = ThisRegion[BLOBSUMYY];
double SumXY = ThisRegion[BLOBSUMXY];
// Get averages
SumX /= Area;
SumY /= Area;
SumXX /= Area;
SumYY /= Area;
SumXY /= Area;
// Create moments
SumXX -= SumX * SumX;
SumYY -= SumY * SumY;
SumXY -= SumX * SumY;
if(SumXY > -1.0E-14 && SumXY < 1.0E-14) SumXY = (float) 0.0; // Eliminate roundoff error
ThisRegion[BLOBSUMX] = SumX;
ThisRegion[BLOBSUMY] = SumY;
ThisRegion[BLOBSUMXX] = SumXX;
ThisRegion[BLOBSUMYY] = SumYY;
ThisRegion[BLOBSUMXY] = SumXY;
System.arraycopy(ThisRegion,0,RegionData[Label],0,BLOBDATACOUNT); // RegionData[Label] <- ThisRegion;
}
// Adjust border region
BorderRegion = RegionData[0];
BorderRegion[BLOBSUMXX] = BorderRegion[BLOBSUMYY] = BorderRegion[BLOBSUMXY] = 0; // Mark invalid fields
System.arraycopy(BorderRegion,0,RegionData[0],0,BLOBDATACOUNT); // RegionData[0] <- BorderRegion;
return MaxLabel;
}
// Sort RegionData array on any column. (I couldn't figure out how to use the built-in java sort.)
static double iField, jField;
static double [] iProperty, jProperty;
public static void SortRegions(int Col)
{
for(int i = 0; i < MaxLabel; i++)
{
for(int j = i+1; j <= Blobs.MaxLabel; j++)
{
iProperty = RegionData[i];
jProperty = RegionData[j];
iField = iProperty[Col];
jField = jProperty[Col];
if(iField > jField)
{
RegionData[i] = jProperty;
RegionData[j] = iProperty;
}
}
}
}
}