Connected Components

Download Jave code from src4.zip

Original and labeled image

   

C:\ece538\2019\impro4>java -jar Connect1.jar
image size: 200 x 112
3 Objects found

Use listAreas.java to calculate object areas (number of pixels)

C:\ece538\2019\impro4>java -jar listAreas.jar
704 576 1120


Connect1.java

import java.util.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;

public class Connect1  {

	static int passes;

   public static void main(String [] args) {
         // load the image
	String filename = args.length>0? args[0]: "uvwb.png";
	int mag = args.length>1? Integer.parseInt(args[1]): 1;
	passes  = args.length>2? Integer.parseInt(args[2]): 2;

	if (passes!=1) passes=2;
	String[] tokens = filename.split("[.]");
	String outfile = tokens[0]+"_pass"+passes;

	 ImageFrame f1 = new ImageFrame(filename);
	 f1.setLocation(100,100);
	 f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	BufferedImage out1 = convert1(f1.img);
        ImageFrame.writeImage(out1,"raw","png");

	BufferedImage out2 = label2rgb(out1);

	 ImageFrame f2 = new ImageFrame(out2,"labeled");
	 f2.setLocation(140+f1.getWidth(),100);
	 f2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 	 f2.writeImage(out2,"labeled","png");
		
    }

    public static BufferedImage convert1(BufferedImage img) {

        int w1 = img.getWidth();
        int h1 = img.getHeight();
	System.out.println("image size: " + w1 + " x " + h1);

	int type = BufferedImage.TYPE_INT_RGB;
        BufferedImage out = new BufferedImage(w1, h1, type);// new image

        int  v, r, g, b, c, n;
	int value[] = new int[w1];
	ArrayList<Run> A = new ArrayList<Run>();
	ArrayList<Run> B = new ArrayList<Run>();
	Run rn=null;
	boolean active = false;
	int count = 0;
	ArrayList<Merge> mlist = new ArrayList<Merge>();
       for (int j = 0; j < h1; j++) {
           img.getRGB(0, j, w1, 1, value, 0, w1); // read and store pixel values
       	   for (int i = 0; i <= w1; i++) {
		try { v = value[i] & 0xFFFFFF; }
		catch(ArrayIndexOutOfBoundsException e) {v=0;}

		if (!active && v>0) {
			active = true;
			rn = new Run(i);
			B.add(rn);
		}		
		else if (active && v==0) {
		        rn.nlast = i-1;
			active = false;
			check_overlap(rn,A,mlist);
			if (rn.nlabel==0) rn.nlabel = ++count;
		}
            }
	    for (Run rb: B) {
		c = rb.nlabel;
		for (int i=rb.nfirst; i<=rb.nlast; i++) value[i] = c;
	    }
	    out.setRGB(0, j, w1, 1, value, 0, w1);
 
	    // exchange lists
	    ArrayList<Run> swp = A;
	    A = B;
            B = swp;
	    B.clear();
       }
	int nobjs = count;
	if (mlist.isEmpty()) {
	    System.out.println(nobjs + " Objects found (no merges)");
	    return out;
	}
	// show results of pass 1 if requested
	if (passes==1) return out;


	boolean seelookup = false;
	
	if (seelookup) System.out.println(mlist.size()+" merges found");
	
	int [] lookup = new int[count+1];
	for (int i=0; i<=count; i++) lookup[i] = i;
	if (seelookup) showtable(lookup,"start");
	for (Merge m: mlist) {
		m.doMerge(lookup);
		if (seelookup) showtable(lookup,""+m);
	}
     	renumber(lookup);
	if (seelookup) showtable(lookup,"renumber");
	nobjs = 0;
	for (int k: lookup) nobjs = k>nobjs? k: nobjs;
        for (int j = 0; j < h1; j++) {
           out.getRGB(0, j, w1, 1, value, 0, w1);
       	   for (int i = 0; i < w1; i++) {
		v = value[i]&0xFFFFFF;
		try { value[i] = lookup[v]; }
		catch(ArrayIndexOutOfBoundsException e) {
			value[i] = 0;
                }
	   }
	   out.setRGB(0, j, w1, 1, value, 0, w1);
	}

	System.out.println(nobjs + " Objects found");
	return out;
    }


   static void showtable(int [] table,String title) {
	System.out.println(title);
   	for (int i=1; i<table.length; i++) {
		System.out.format("%3d ",table[i]);
		if ((i%16)==0) System.out.println();
    	}
	System.out.println();
   }

    static void renumber(int [] table) {
    	for (int i=1; i<table.length; i++) {
		if (missing(table, i)) {
		int nt = nextfound(table,i);
		for (int k=1; k<table.length; k++) {
			if (table[k]==nt) table[k] = i;
		   }
		}
	 }
    }



   static int nextfound(int [] table, int nq) {
	for (int n: table) {
		if (n>nq) return n;
	}
	return -1;
  }

   static boolean missing(int [] table, int nq) {
	for (int n: table) 
		if (n==nq) return false;	
	return true;
   }


    static void check_overlap(Run rn, ArrayList<Run> A, ArrayList<Merge> mlist) {
	for (Run ra: A) {
	  int n = Run.compare(rn,ra);
   
	  // n==0 means runs overlap
	  if (n==0) {
	     if (rn.nlabel==0) rn.nlabel = ra.nlabel;
	     else if (rn.nlabel != ra.nlabel) {
		Merge m = new Merge(ra,rn);
		boolean isdup = false;
		for (Merge mt: mlist) {
			if (m.equals(mt)) { isdup = true; break; }
		}
		if (!isdup) mlist.add(m);
	     }
	  }
	  // n<0 means remaining runs are to right
	  else if (n<0) break;
	}
    }

    public static BufferedImage label2rgb(BufferedImage img) {

        int w1 = img.getWidth();
        int h1 = img.getHeight();

	int type = BufferedImage.TYPE_INT_RGB;
        BufferedImage out = new BufferedImage(w1, h1, type);// new image
	int [] colormap = {0xFFFF0000,0xFF00FF00,0xFF0000FF,0xFFFFFF00,0xFF00FFFF,0xFFFF00FF};
	int clen = colormap.length;
	int value[] = new int[w1];
	int v;
	for (int j = 0; j < h1; j++) {
           img.getRGB(0, j, w1, 1, value, 0, w1); // read and store pixel value
       	   for (int i = 0; i < w1; i++) {
		try { v = value[i] & 0xFFFFFF; }
		catch(ArrayIndexOutOfBoundsException e) {v=0;}
 		if (v>0) value[i] = colormap[(v-1)%clen];
	   }
	   out.setRGB(0, j, w1, 1, value, 0, w1);
	}
	return out;
   }
}

class Run {
   int nfirst, nlast, nlabel;
   Run(int nstart) {
	nfirst = nstart;
    }
    public String toString() {
	return nlabel + "  " + nfirst + " : "+nlast;
    }
    public static int compare(Run ra, Run rb) {
	if (ra.nlast <rb.nfirst-1) return -1; // ra is left of rb
	if (ra.nfirst>rb.nlast+1) return 1; // ra is right of rb
	return 0; // overlap
    }
}

class Merge {
    int na, nb;
    Merge(Run ra, Run rb) {
	na = ra.nlabel;
	nb = rb.nlabel;
    }
    public String toString() {
	return "merge " + na + " with " + nb;
    }

    public boolean equals(Merge t) {
	if (na==t.na && nb==t.nb) return true;
	if (na==t.nb && nb==t.na) return true;
	return false;
    }

    public void doMerge(int [] lookup) {
	int ma = lookup[na];
	int mb = lookup[nb];
	for (int i=0; i<lookup.length; i++) {
		if (lookup[i]==ma) lookup[i]=mb;
	}
    }
}




Maintained by John Loomis, updated Wed Apr 17 12:31:04 2019