package oculusPrime; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; public class OculusImage { private int[] parr; // working pixels, whole image, 8-bit greyscale OR 1 bit B&W private int width; private int height; public int lastThreshhold = -1; private float threshholdMult; // = 0.65; private float lastBlobRatio; private float lastTopRatio; private float lastBottomRatio; private float lastMidRatio; private int[] parrorig; private int imgaverage; public OculusImage() { } public void dockSettings(String str) { String[] a = str.split("_"); lastBlobRatio = Float.parseFloat(a[0]); lastTopRatio = Float.parseFloat(a[1]); lastMidRatio = Float.parseFloat(a[2]); lastBottomRatio = Float.parseFloat(a[3]); } private void convertToGrey(int[] pixelRGB) { // uses 30-59-11 RGB weighting from: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale int p; parr = new int[width*height]; int n = 0; int runningttl = 0; for (int i=0; i < pixelRGB.length; i++) { int red = (pixelRGB[i] & 0x00ff0000) >> 16; int green = (pixelRGB[i] & 0x0000ff00) >> 8; int blue = pixelRGB[i] & 0x000000ff; p = (int) (red*0.3 + green*0.59 + blue*0.11) ; parr[n]=p; n++; runningttl += p; } imgaverage = runningttl/n; threshholdMult = (float) (0.65 - 0.2 + (0.40*( imgaverage/255))); } private void sendToImage(int[] pixelRGB) { // dev tool // Util.debug("sendtoImage "+pixelRGB.length,this); // Application.processedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // for(int y=0; y<height; y++) { // for (int x=0; x<width; x++) { // int grey = pixelRGB[x + y*width]; // if (grey==1) {grey=255;} // only if psuedo-boolean parr // int argb = (grey<<16) + (grey<<8) + grey; // Application.processedImage.setRGB(x, y, argb); // } // } } private void sendToImage(Boolean[] pixelRGB) { // dev tool Util.debug("sendtoImageBoolean "+pixelRGB.length,this); Application.processedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for(int y=0; y<height; y++) { for (int x=0; x<width; x++) { int p = x + y*width; int argb; if (pixelRGB[p]==true) { argb = (255<<16) + (0<<8) + 0; } // fillsize ++; } // red else { int grey = parrorig[p]; argb = (grey<<16) + (grey<<8) + grey; } Application.processedImage.setRGB(x, y, argb); } } } private Boolean[] floodFill(int[] ablob, int start) { ArrayList<Integer> q= new ArrayList<Integer>(); q.add(start); Boolean[] blob = new Boolean[0]; try { blob = new Boolean[width*height]; } catch (Exception e) { // probably heap overflow CATCH NOT WORKING Util.log("floodFill()", e, this); // e.printStackTrace(); return new Boolean[0]; } Arrays.fill(blob, false); int n; int w; int e; int i; while (q.size() > 0) { n = q.remove(q.size()-1); if (ablob[n]==1) { w = n; e = n; while (ablob[w]==1) { w --; if(w<0 || w>=ablob.length) {break;} } while (ablob[e]==1) { e ++; if(e<0 || e>=ablob.length) {break;}} for (i=w+1; i<=e-1; i++) { ablob[i]=0; blob[i]=true; if (i-width<ablob.length && i-width>0) { if (ablob[i-width]==1) { q.add(i-width); } } if (i+width<ablob.length && i+width>0) { if (ablob[i+width]==1) { q.add(i+width); } } } } } return blob; } public String[] findBlobStart(int x, int y, int w, int h, int[] bar) { // calibrate only... lastThreshhold = 0; String r[]; // findBlobStartSub(x, y, w, h, bar); r = findBlobStartSub(x,y,w,h,bar); // do it again, with contrast averaged return r; } private String[] findBlobStartSub(int x, int y, int w, int h, int[] bar) { // calibrate sub width = w; height = h; convertToGrey(bar); parrorig = parr.clone(); // save original image for re-threshholding after int start = x + y*width; String[] result = new String[]{"0","0","0","0","0","0","0","0","0"}; int startavg = (parr[start-1]+parr[start]+parr[start+1])/3; //includes 2 adjacent pixels in contract threshhold to counteract grainyness a bit int threshhold = (int) (startavg*threshholdMult); if (lastThreshhold !=0) { threshhold = lastThreshhold; } int i; for (i=0;i<parr.length;i++){ if (parr[i]>threshhold) { parr[i]=1; } else { parr[i]=0; } } Boolean[] blob = floodFill(parr, start); int blobSize =0; int r[] = getRect(blob,start); int minx = r[0]; int maxx = r[1]; int miny = r[2]; int maxy = r[3]; blobSize = r[4]; int blobBox = (maxx-minx)*(maxy-miny); lastTopRatio = (float) getPixelEqTrueCount(blob, minx, (int) (minx+(maxx-minx)*0.333), miny, maxy) / (float) blobBox; // left lastMidRatio = (float) getPixelEqTrueCount(blob, (int) (minx+(maxx-minx)*0.333), (int) (minx+(maxx-minx)*0.666), miny, maxy) / (float) blobBox; lastBottomRatio = (float) getPixelEqTrueCount(blob, (int) (minx+(maxx-minx)*0.666), maxx, miny, maxy) / (float) blobBox; // left lastBlobRatio = (float)(maxx-minx)/(float)(maxy-miny); float slope = getBottomSlope(blob,minx,maxx,miny,maxy)[0]; //result = x,y,width,height,slope,lastBlobRatio,lastTopRatio,lastMidRatio,lastBottomRatio result = new String[]{Integer.toString(minx), Integer.toString(miny), Integer.toString(maxx-minx), Integer.toString(maxy-miny), Float.toString(slope), Float.toString(lastBlobRatio), Float.toString(lastTopRatio), Float.toString(lastMidRatio), Float.toString(lastBottomRatio)}; if (lastThreshhold==0) { int runningttl = 0; for (i=0; i<width*height; i++) { // zero to end if (blob[i]) { runningttl += parrorig[i]; } } lastThreshhold = (int) ((runningttl/blobSize)*threshholdMult); // adaptive threshhold } sendToImage(blob); return result; } public String[] findBlobs(int[] bar, int w, int h) { width = w; height = h; int attemptnum = 0; int dir = -1; int inc = 10; int n = inc; int deleteddir = 0; String[] result = new String[]{"0","0","0","0","0"}; //x,y,width,height,slope while (attemptnum < 15) { // was 15 result = findBlobsSub(bar); if (result[2].equals("0")) { if (deleteddir != 0) { n = inc; if (deleteddir == -1) { dir = 1; } else { dir =-1; } } else { dir = dir*(-1); } lastThreshhold = lastThreshhold + (n*dir); if (lastThreshhold < 0) { dir = 1; deleteddir = -1; lastThreshhold = n; } if (lastThreshhold > 255) { dir = -1; deleteddir = 1; lastThreshhold = 255-n; } n += inc; } else { break; } attemptnum ++; } return result; } public String[] findBlobsSub(int[] bar) { String[] result = new String[]{"0","0","0","0","0"}; //x,y,width,height,slope convertToGrey(bar); parrorig = parr.clone(); int minimumsize = (int) (width*height*0.002); if (lastThreshhold == -1) {lastThreshhold = imgaverage; } int threshhold = lastThreshhold; int i; int[] parrinv = new int[width*height]; // inverse, used to check for inner black blob for (i=0; i<parr.length; i++){ //convert to B&W if (parr[i]>threshhold) { parr[i]=1; parrinv[i]=0; } else { parr[i]=0; parrinv[i]=1; } } int blobnum = 0; float maxdiff = 99.0f; float diff; float slope = -1; int winner =-1; int[] winRect = new int[]{0,0,0,0,0}; int minx = 0; int miny = 0; int maxx = 0; int maxy = 0; float topRatio; float bottomRatio; float midRatio; int blobSize; int[] r; int pixel; ArrayList<Boolean[]> blobs = new ArrayList<Boolean[]>(); int blobBox; ArrayList<Integer> blobstarts = new ArrayList<Integer>(); for (pixel=0; pixel<width*height; pixel++) { // zero to end, find all blobs if (parr[pixel]==1) { // finds a white one Boolean[] temp = floodFill(parr, pixel); if (temp.length > minimumsize) { // discard tiny blobs was 150 blobs.add(temp); blobstarts.add(pixel); if (blobs.size() > 255) { // probably noisy img, avoid heap overflow Util.log("error, too many blobs", this); return result; } } } } ArrayList<Integer> rejectedBlobs = new ArrayList<Integer>(); while (rejectedBlobs.size() < blobs.size()) { for (blobnum=0; blobnum<blobs.size(); blobnum++) { // go thru and eval each blob if (rejectedBlobs.indexOf(blobnum) == -1) { r = getRect(blobs.get(blobnum),blobstarts.get(blobnum)); blobSize = r[4]; minx = r[0]; maxx = r[1]; miny = r[2]; maxy = r[3]; blobBox = (maxx-minx)*(maxy-miny); topRatio = (float) getPixelEqTrueCount(blobs.get(blobnum), minx, (int) (minx+(maxx-minx)*0.333), miny, maxy) / (float) blobBox; midRatio = (float) getPixelEqTrueCount(blobs.get(blobnum), (int) (minx+(maxx-minx)*0.333), (int) (minx+(maxx-minx)*0.666), miny, maxy) / (float) blobBox; bottomRatio = (float) getPixelEqTrueCount(blobs.get(blobnum), (int) (minx+(maxx-minx)*0.666), maxx, miny, maxy) / (float) blobBox; float blobRatio = (float) (maxx-minx)/(float)(maxy-miny); diff = Math.abs(topRatio - lastTopRatio) + Math.abs(bottomRatio- lastBottomRatio) + Math.abs(midRatio- lastMidRatio); if (diff < maxdiff && blobRatio <= lastBlobRatio*1.1) { winner=blobnum; maxdiff = diff; winRect = r.clone(); } } } if (winner == -1) { break; } else { // best looking blob chosen, now check if it has ctr blob AND bottom slope extents wider than rest minx = winRect[0]; maxx = winRect[1]; miny = winRect[2]; maxy = winRect[3]; int ctrx = minx+((maxx-minx)/2); int ctry = miny+((maxy-miny)/2); i = ctrx + ctry*width; // dead center of winner blob if (parrinv[i]==1) { // if ctr blob start exists Boolean[] ctrblob = floodFill(parrinv,i); r = getRect(ctrblob,i); if (minx<r[0] && maxx>r[1] && miny<r[2] && maxy>r[3] && r[4] > 10 && r[4]<winRect[4]*0.5 && r[4]>winRect[4]*0.2 ) { // ctrblob completely within blob float[] sl = getBottomSlope(blobs.get(winner),minx,maxx,miny,maxy); slope = sl[0]; if (sl[1]<=minx*0.9 || sl[2]>=maxx*0.9) { // bottom slope is widest on at least one side break; } // else { Util.debug("failed slope test",this); sendToImage(blobs.get(winner)); } } } rejectedBlobs.add(winner); winner = -1; } } if (winner != -1) { result = new String[]{Integer.toString(minx),Integer.toString(miny),Integer.toString(maxx-minx), Integer.toString(maxy-miny),Float.toString(slope)}; //x,y,width,height,slope blobSize = winRect[4]; int runningttl = 0; for (pixel=0; pixel<width*height; pixel++) { // zero to end if (blobs.get(winner)[pixel]) { runningttl += parrorig[pixel]; } } lastThreshhold = (int) ((runningttl/blobSize)*threshholdMult); // adaptive threshhold // sendToImage(blobs.get(winner)); // testing } else { // sendToImage(parrorig); // testing } return result; } private int[] getRect(Boolean[] blob, int start) { int y = start/width; int x = start - (y*width); int minx = x; int miny = y; int maxx = x; int maxy = y; int p; int tempy; int tempx; int size = 0; for (p=0; p<width*height; p++) { if (blob[p]) { tempy = p/width; tempx = p - (tempy*width); if (tempx < minx) { minx = tempx; } if (tempx > maxx) { maxx = tempx; } if (tempy < miny) { miny = tempy; } if (tempy > maxy) { maxy = tempy; } size++; } } int[] result = {minx,maxx,miny,maxy,size}; return result; } private int getPixelEqTrueCount(Boolean[] blob, int startx, int endx, int starty, int endy) { int result = 0; for (int yy = starty; yy<endy; yy++) { for (int xx = startx; xx<=endx; xx++) { if (blob[yy*width + xx]==true) { result++; } } } // Util.debug("result: "+result, this); return result; } private float[] getBottomSlope(Boolean[] blob, int minx, int maxx, int miny, int maxy) { int start = -1; for (int i = maxx+maxy*width; i>=minx+miny*width; i-=1) { if (blob[i]) { start = i; break; } } int starty = start/width; int startx = start-(starty*width); int direction = 1; if (startx > minx+(maxx-minx)/2) { direction = -1; } if (direction == -1) { while (blob[start+1]) { start ++; } } else { while (blob[start-1]) { start -= 1; } } int end = start; while (blob[end + direction] || blob[end-width+direction]) { //crawl up diagonally or flat until hit vert wall end += direction; if (!blob[end]) { end -= width; } } int endy = end/width; int endx = end-(endy*width); float rightx = endx; float leftx = startx; if (direction == -1) { rightx = startx; leftx = endx; } float[] result = new float[]{(float) (endy-starty)/(float)(endx-startx), leftx, rightx }; return result; } }