All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bytedeco.javacv.Blobs Maven / Gradle / Ivy

There is a newer version: 1.5.11
Show newest version
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;
                }
            }
        }
    }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy