package com.hygenics.facialrec; import imagetools.FileTypeException; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfRect; import org.opencv.core.Point; import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.highgui.Highgui; import org.opencv.objdetect.CascadeClassifier; /** * This is not mine. This is from OpenCV due to the need to use a training set to find * relevant statistics. You can provide any cascade (preferably haar) to find nearly anything but it has * been found that nearly 1,000,000 images are needed to reach above 90% accuracy * in detection if the item to detect is generic (i.e. any pen instead of my Pentel pen). * * Provide a cascade xml from the OpenCV trainer and a fpath to use this class. * * The class can be used to get an arraylist of possible objects in BufferedImage form. * I place the triangles in a new image which is written to a buffered image. * * Note: The default cascade resides at * * WARNING: There is a class that can take and spline an object with a set of points but the spline * is part of my open source package. The basics (splining,harr through opencv;etc.) are all open source. * I don't believe in giving up control of the basics, only that which is business specific (your own ideas). * * * @author OpenCV project * */ public class FindObjects{ private boolean display=false; private boolean grey=false; private boolean save=false; private boolean saveeach=false; private Map<FoundObject,BufferedImage> objects=new HashMap<FoundObject, BufferedImage>(); private ArrayList<BufferedImage> images=new ArrayList<BufferedImage>(); private String fpath; private int numobjects=0; private String cascade="C:\\Users\\aevans\\Documents\\OpenCV_2_8_0\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml"; private boolean combine=false; public FindObjects(){ } public boolean isDisplay() { return display; } public void setDisplay(boolean display) { this.display = display; } public boolean isCombine() { return combine; } public void setCombine(boolean combine) { this.combine = combine; } public boolean isGrey() { return grey; } public void setGrey(boolean grey) { this.grey = grey; } public ArrayList<BufferedImage> getImages() { if(images.size()>0) { images=new ArrayList<BufferedImage>(); } for(BufferedImage img: objects.values()) { images.add(img); } return images; } public void setImages(ArrayList<BufferedImage> images) { this.images = images; } public String getFpath() { return fpath; } public void setFpath(String fpath) { this.fpath = fpath; } public String getCascade() { return cascade; } public void setCascade(String cascade) { this.cascade = cascade; } public boolean isSave() { return save; } public void setSave(boolean save) { this.save = save; } public int getNumobjects() { return numobjects; } public void setNumobjects(int numobjects) { this.numobjects = numobjects; } public boolean isSaveeach() { return saveeach; } public void setSaveeach(boolean saveeach) { this.saveeach = saveeach; } private void display(){ JFrame frame; for(BufferedImage img : objects.values()){ frame=new JFrame(); frame.add(new JLabel(new ImageIcon(img))); frame.pack(); frame.setFocusable(true); frame.setFocusableWindowState(true); frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); frame.setVisible(true); } } private void findObjects(){ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); //Create System.out.println("Running Detection"); if(cascade.toLowerCase().contains(".xml")==true) { CascadeClassifier faceDetector = new CascadeClassifier(cascade); if( faceDetector.empty()==false) { //read image if cascade found Mat image = Highgui.imread(fpath); //face detect if(image.empty()==false) { System.out.println("Finding Faces"); MatOfRect faceDetections = new MatOfRect(); faceDetector.detectMultiScale(image, faceDetections); System.out.println("Found "+faceDetections.toArray().length+" objects."); BufferedImage bi; boolean addObject=true; FoundObject fo; int width; int height; int startx; int starty; //Grabbing Rectangle tile maps of faces and create image with them for (Rect rect : faceDetections.toArray()) { numobjects++; height=rect.height; width=rect.width; startx=rect.x; starty=rect.y; //check to ensure that there are not nested objects //remove any objects that are found to be nested if(objects.size()>0) { for(FoundObject face : objects.keySet()){ //get the maximum and minimum width for intersection checking int longerwidth=Math.max(face.width, rect.width); int smallerwidth=Math.min(face.width,rect.width); //get the maximum and minimum height for intersection checking int largerheight=Math.max(face.height, rect.height); int smallerheight=Math.min(face.height,rect.height); //get the maximum and minimum starting point int longerx=(face.width==longerwidth)?face.x:rect.x; int shorterx=(face.width==smallerwidth)?face.x:rect.x; //get the longer and shorter y values int longery=(face.height==largerheight)?face.y:rect.y; int shortery=(face.height==smallerheight)?face.y:rect.y; if(face.x<=rect.x & face.y <= rect.y & (face.x+face.width)>= (rect.x+rect.width) & (face.y+face.height)<=(rect.y+rect.height)){ //case that new rectangle is nested addObject=false; } else if(rect.x<=face.x & rect.y <= face.y & (rect.x+rect.width)>=(face.x+face.width) & (rect.y+rect.height)>=(face.y+face.height)){ //case that old rectangle is nested objects.remove(face); } else if(combine==true & ((((longerx-shorterx)<= longerwidth & (longerx-shorterx)<(longerx-shorterx+smallerwidth)) | ((longerx-shorterx+smallerwidth)<longerwidth & (longerx-shorterx+smallerwidth)<(longerx-shorterx)) | (((longerx-shorterx)<= longerwidth & (longerx-shorterx)<(longerx-shorterx+smallerwidth)))) & (((longery-shortery+smallerheight)<largerheight & (longery-shortery+smallerheight)<(longery-shortery))) )){ //after checking that the positions are within a certain range, check for overlap //will eliminate overlap only if told to do so if(Math.abs(longerx-shorterx)<=longerwidth & Math.abs(longery-shortery)<=largerheight & Math.abs((longerwidth+longerx)-(shorterx+smallerwidth))<=longerwidth) { //case for faces overlapping left side: this is where the integers make this smaller //reset the combined width and height -->this will be our worst case scenario if(shorterx<longerx) { width=(longerx-shorterx)+longerwidth; } else { width=((longerx+longerwidth)<(shorterx+smallerwidth))?(shorterx-longerx)+smallerwidth:longerwidth; } if(shortery<longery) { height=(longery-shortery)+largerheight; } else { height=((longery+largerheight)<(shortery+smallerheight))?(shortery-longery)+smallerheight:largerheight; } //reset the start x and y positions startx=(longerx<=shorterx)?longerx:shorterx; starty=(longery<=shortery)?longery:shortery; //remove the overlapping face objects.remove(face); } } } } if(addObject) { fo=new FoundObject(); fo.setX(rect.x); fo.setY(rect.y); fo.setHeight(rect.height); fo.setWidth(rect.width); if(grey){ bi=new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR); } else{ bi=new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR); } int x=0; int y=0; boolean set=false; for(int w=startx;w<(startx+width);w++) { for(int h=starty;h<(starty+height);h++) { //get the color which is natively in bgr format if(w>=0 & h>=0 & h<image.height() & w<image.width()) { double[] rgb=image.get(w, h); Color c=new Color((int)rgb[2],(int)rgb[1],(int)rgb[0]); bi.setRGB(x, y, c.getRGB()); y++; set=true; } } //increment to next row in new image if anything was set and reset the set boolean if(set) { x++; set=false; } } //add our new image to the array associated with its points object objects.put(fo,bi); //if a save is requested mark the found face with a green box if(save) { Core.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height),new Scalar(0, 255, 0)); } } //reset add object, the check to see if the new object is nested within an old one addObject=true; } //display all images if requested if(display) { display(); } //save the overall image with all faces marked by green boxese on request if(save) { //output test image String filename = fpath.replaceAll("\\..*","FULLTESTOUTPUT.jpg"); System.out.println(String.format("Writing %s", filename)); Highgui.imwrite(filename, image); } } else { try{ throw new FileNotFoundException("Could Not find Image fpath."); }catch(FileNotFoundException e) { e.printStackTrace(); } } }else{ try{ throw new FileNotFoundException("Could Not Find Cascade"); }catch(FileNotFoundException e){ e.printStackTrace(); } } }else{ try{ throw new FileTypeException("Cascade file type must be XML."); }catch(FileTypeException e) { e.printStackTrace(); } } } public void run() { findObjects(); } }