Com a biblioteca OpenCv possível fazer bastante coisa, o foco desse post é a detecção facial. Para quer tiver interessado em conhecer um pouco mais: http://opencv.org/
Vamos utilizar Java que é umas das linguagens suportadas. Como vamos brincar com essa biblioteca iremos construir um aplicativo para desfocar apenas os rotos das pessoas em fotos:
"Chaves de menor":
Atenção, Creuzebek!
Faça do download da biblioteca no site do OpenCv: http://opencv.org/. Crie um projeto Java no Eclipse.
Clique com o potão direito sobre o seu projeto vá em: Build Path -> Configure Build Path,
espanda o item referente a JRE clique em edit e selecione o diretório
onde estão as bibliotecas nativas do OpenCv que por padrão estão em C:/opencv/opencv/build/java/x86,
mas pode mudar dependendo da sua instalação.
Nesse projeto eu utilizei os conceitos do SOLID, que bem resumidamente diz que cada classe deve servir a um único propósito. Dessa forma essa ficou sendo a minha estrutura de pacotes:
Para começar coloque a linha a seguir no main do seu código antes de qualquer outro trecho:
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Essa linha é responsável por "invocar" as bibliotecas nativas que inserimos anteriormente, que na realidade é quem faz todo o trabalho. Logo após você precisará carregar um arquivo xml que contém as parametrizações necessárias para fazer o reconhecimento dos rostos:
CascadeClassifier cascadeClassifier = new CascadeClassifier(System.getProperty("user.dir") + "/haarcascade_frontalface_alt_tree.xml");
Bom aí você me pergunta onde encontro esse arquivo? dependendo da sua instalação ele fica arqui: C:\opencv\opencv\sources\data\haarcascades . Sugiro que você copie esse arquivo para raiz do seu projeto e carregue ele como está acima.
Após isso precisamos carregar a imagem, você deve fazer dessa forma:
Mat mat = Highgui.imread(System.getProperty("user.dir") +"/chaves.jpg");
No método imread você passa o caminho até o arquivo.
Para facilitar o entendimento eu crie um diagrama de atividades que ilustra os passos necessários para a brincadeira acontecer:
Passo 1 - Detectar faces
Para essa atividade ou passo está na classe ServiceDeteccaoFacesImagem e temo o método que faz a detecção das faces:
public MatOfRect detectarFaces(CascadeClassifier cascadeClassifier, Mat mat){
MatOfRect matOfRect = new MatOfRect();
cascadeClassifier.detectMultiScale(mat, matOfRect);
return matOfRect;
}
Esse método recebe os dois objeto criados anteriormente, que são respectivamente: a classe que "carrega" o xml e a classe que "carrega" a imagem. o objeto CascadeClassifier contém o método detectMultScale que localiza o que está sendo procurado na imagem que em nosso caso são rostos.
Passo 2 - Obter dados das faces
Os dados que serão obtidos das faces serão sua posição (x,y) na imagem e a largura e altura da face. O método a seguir contém a lógica de extração desses dados novamente a classe utilizada é ServiceDeteccaoFacesImagem:
public List obterDadosFaces(MatOfRect matOfRect){
List dados = new ArrayList();
for (Rect rect : matOfRect.toArray()) {
PropriedadesFace prop = new PropriedadesFace();
prop.setX(rect.x);
prop.setY(rect.y);
prop.setHeight(rect.height);
prop.setWidth(rect.width);
dados.add(prop);
}
return dados;
}
Notem que o retorno é uma lista do objeto PropriedadesFace abaixo o modelo da classe desse objeto, ocultado os geters e seteres:
public class PropriedadesFace {
private int x;
private int y;
private int width;
private int height;
private BufferedImage imageCortada;
Passo 3 - Desfocar toda a imagem
Vamos desfocar a imagem antes de "recortar" as faces, essa atividade está na classe ServiceDesfoqueImagem:
public BufferedImage DesfocarImagem(Mat mat){
mat = Desfocar(mat);
return Util.converterParaImage(mat);
}
private Mat Desfocar(Mat image){
Mat destination = new Mat(image.rows(),image.cols(),image.type());
Imgproc.GaussianBlur(image, destination,new Size(45,45), 0);
return destination;
}
Temos aqui esses 2 métodos um recebe o objeto Mat e o outro faz o desfoque da imagem. O desfoque é feito utilizado o método GaussianBlur da classe Imgproc, que pode ser invocado diretamente porque é um método estático. Por fim convertemos o objeto Mat para o BufferedImage, esse método se encontra na classe Util:
public static BufferedImage converterParaImage(Mat image){
MatOfByte bytemat = new MatOfByte();
Highgui.imencode(".jpg", image, bytemat);
byte[] bytes = bytemat.toArray();
InputStream in = new ByteArrayInputStream(bytes);
BufferedImage img=null;
try {
img = ImageIO.read(in);
} catch (IOException e) {
e.printStackTrace();
}
return img;
}
Passo 4 - Recortar as faces da imagem
Para cortar as faces da imagem utilizamos o método CortarImagem da classe ServiceCorteImagem:
public List CortarImagem(List dados, BufferedImage imagem){
for(PropriedadesFace dado : dados){
dado.setImageCortada(imagem.getSubimage(dado.getX(), dado.getY(), dado.getWidth(), dado.getHeight()));
}
return dados;
}
Utilizamos o método getSubImage para "cortar" uma determinada parte da imagem e a imagem "cortada" é incluída no objeto PropriedadesFace, o mesmo objeto que contém seus dados.
Passo 5 - Obter imagem sem o desfoque
Para obter essa imagem utilizamos o método converterParaImagem na classe Util:
public static BufferedImage converterParaImage(Mat image){
MatOfByte bytemat = new MatOfByte();
Highgui.imencode(".jpg", image, bytemat);
byte[] bytes = bytemat.toArray();
InputStream in = new ByteArrayInputStream(bytes);
BufferedImage img=null;
try {
img = ImageIO.read(in);
} catch (IOException e) {
e.printStackTrace();
}
return img;
}
Essa imagem vai servir como fundo para "colarmos" as imagens dos rostos desfocados sobre ela.
Passo 6 - Juntar imagem com as faces recortadas
Utilizamos esse dois métodos para sobrepor as imagens:
public BufferedImage juntarImagens(List dados, BufferedImage imagemPrincipal){
for(PropriedadesFace dado: dados){
imagemPrincipal = juntarUmaImage(imagemPrincipal, dado.getImageCortada(),dado.getX(),dado.getY());
}
return imagemPrincipal;
}
public static BufferedImage juntarUmaImage(BufferedImage imagemPrincipal,
BufferedImage imagemCortada, int x, int y) {
Graphics2D g = imagemPrincipal.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(imagemPrincipal, 0,0, null);
g.drawImage(imagemCortada, x, y, null);
g.dispose();
return imagemPrincipal;
}
O primeiro percorre todas as imagens que estão juntas com os dados das suas posições, já o segundo faz a sobreposição em si. Os dois métodos estão na classe ServiceSobreposicaoImagem.
Para terminar
Esse é o código do método main responsável por sequenciar as chamadas dos métodos:
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
CascadeClassifier cascadeClassifier = new CascadeClassifier(System.getProperty("user.dir") + "/haarcascade_frontalface_alt_tree.xml");
Mat mat = Highgui.imread(System.getProperty("user.dir") +"/chaves.jpg");
ServiceDeteccaoFacesImagem serviceExtractFaces = new ServiceDeteccaoFacesImagem();
MatOfRect matOfRect = serviceExtractFaces.detectarFaces(cascadeClassifier, mat);
List propsFaces = serviceExtractFaces.obterDadosFaces(matOfRect);
ServiceDesfoqueImagem serviceBlur = new ServiceDesfoqueImagem();
BufferedImage imagemCorteDesfoque = serviceBlur.DesfocarImagem(mat);
ServiceCorteImagem serviceCrop = new ServiceCorteImagem();
propsFaces = serviceCrop.CortarImagem(propsFaces, imagemCorteDesfoque);
ServiceSobreposicaoImagem serviceOverlay = new ServiceSobreposicaoImagem();
BufferedImage imagemSemEfeitos = Util.converterParaImage(mat);
imagemCorteDesfoque = serviceOverlay.juntarImagens(propsFaces, imagemSemEfeitos);
File outputfile = new File("chaves menor.jpg");
try {
ImageIO.write(imagemCorteDesfoque, "jpg", outputfile);
} catch (IOException e) {
e.printStackTrace();
}