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();
}
}